mirror of
https://github.com/fankes/ColorOSNotifyIcon.git
synced 2025-09-05 10:15:33 +08:00
Compare commits
257 Commits
1.51
...
570e5a23ba
Author | SHA1 | Date | |
---|---|---|---|
570e5a23ba
|
|||
90aec5b9fa
|
|||
7f3cdba757
|
|||
9536b42c2b
|
|||
a7582de004
|
|||
29b14a811e
|
|||
9b9eda8a39
|
|||
cfa065b57e
|
|||
|
492d0dad67 | ||
|
d26f68c1a7 | ||
d9520cb484
|
|||
884aa87617
|
|||
|
51e52f3eba | ||
|
9aae48c785 | ||
ee77842fbb
|
|||
3794035789
|
|||
a8c1ba3166
|
|||
4d39eb4b9d
|
|||
2ffd36cc21
|
|||
d95e53f6e4
|
|||
115ccf0822
|
|||
982f73444a
|
|||
93fdd307a2
|
|||
75789c0286
|
|||
b1ca7be6ed
|
|||
c4087db1e6
|
|||
|
bf69aed1b4 | ||
a0c001b7e4
|
|||
|
1b0fcdfce6 | ||
8d42d47ab4
|
|||
a7c43149e3
|
|||
053c8b837c
|
|||
1fdaf81560
|
|||
67937edd0f
|
|||
bb9e965cca
|
|||
d7e487c4ce
|
|||
11480f6d4a
|
|||
a09d039b48
|
|||
95b49168cb
|
|||
49b942efda
|
|||
025183d8f0
|
|||
78205b2c6c
|
|||
c3ac4d4be1
|
|||
a22d36c79d
|
|||
8889d6fa5f
|
|||
8662637e6a
|
|||
b88b7b6907
|
|||
ae0f163d57
|
|||
757313ee14
|
|||
|
0ee83ff83a | ||
|
29c3a839f4 | ||
faf443960c
|
|||
41cf6a209a
|
|||
71a37d8bf3
|
|||
4afddae4e1
|
|||
3714c3f06d
|
|||
6d624b3992
|
|||
b3a3e0993c
|
|||
c34f121145
|
|||
b2a65a4f3c
|
|||
799d758671
|
|||
9f323f466a
|
|||
713d5bc008
|
|||
e0f50984b0
|
|||
1e0efe8f86
|
|||
358fc16b0e
|
|||
fc69d7b544
|
|||
5e37c1b7dc
|
|||
147a33d8ee
|
|||
75ec1317fa
|
|||
62d4e727ff
|
|||
c4573d221b
|
|||
b74adce241
|
|||
bd57f76232
|
|||
e67110fd65
|
|||
2c48401d21
|
|||
bcac9a3752
|
|||
656fc0446c
|
|||
b4f0b65264
|
|||
a577c49e56
|
|||
1431457e37
|
|||
4edf7a2df2
|
|||
171bbae16b
|
|||
c1d6a85927
|
|||
ea77b3631f
|
|||
8b906032f2
|
|||
6cd9cb79ac
|
|||
8bdff8d558
|
|||
3ccde65af6
|
|||
7503a64efd
|
|||
0d564258c9
|
|||
f172b6b1b9
|
|||
ecf7ed8653
|
|||
1efb18f4e1
|
|||
fa99a57027
|
|||
4d3106f5bc
|
|||
8f735ab23d
|
|||
3503cf35b1
|
|||
1d5759313b
|
|||
7dcd6de33e
|
|||
ff941171f9
|
|||
eeb7966448
|
|||
a1214f0f38
|
|||
855f88127d
|
|||
d589ef2cd0
|
|||
c95c36c76a
|
|||
2eec2022d1
|
|||
4b00d84d8b
|
|||
89767143bd
|
|||
e119fd0bf7
|
|||
bd08b2ce1d
|
|||
a334bf9a8c
|
|||
bad773cc2c
|
|||
e5a2edb614
|
|||
b9a8b6195b
|
|||
0cc499e690
|
|||
df4694bb56
|
|||
c0dbf565e9
|
|||
2f0766cc9f
|
|||
896322361e
|
|||
9e53e0ddba
|
|||
41b9127c15
|
|||
24a028629d
|
|||
70c2f9aa8f
|
|||
4ad7acb56c
|
|||
bae57c87d8
|
|||
7938477bb3
|
|||
1253b8c371
|
|||
dea19c72ab
|
|||
fbce4f9f7a
|
|||
3697a9ad18
|
|||
aaca48bb8e
|
|||
28e579ad0e
|
|||
b8973fa15a
|
|||
3fa8a3c7e9
|
|||
cc2e2df37e
|
|||
6725bf14a5
|
|||
a896806fa5
|
|||
5e56c1bc57
|
|||
3c96c30a74
|
|||
ca4a594114
|
|||
77ff08f6d7
|
|||
4fd017fed1
|
|||
91b1500ebd
|
|||
601ceee89f
|
|||
e33c51a320
|
|||
9c8df9f173
|
|||
1e98c0d431
|
|||
8ef9e206a6
|
|||
ca5da08bc2
|
|||
56da358999
|
|||
53e5292f2e
|
|||
1b4be321ec
|
|||
ddcc475ea1
|
|||
65142c3af7
|
|||
118104c11c
|
|||
1fd9cc6ab0
|
|||
889092dd3d
|
|||
9d695c61f0
|
|||
81a6ada0ee
|
|||
5fb4e0ac39
|
|||
62a7d87502
|
|||
95a9e3253b
|
|||
bff047aa48
|
|||
975ebe7e49
|
|||
eb9c93a2f5
|
|||
ff43641d63
|
|||
22f5ebc625
|
|||
2f302a3c10
|
|||
fa2aa675d0
|
|||
eb090b11de
|
|||
97161f8692
|
|||
6159308cbb
|
|||
be6447342f
|
|||
c0e9ed4673
|
|||
45328bd1a1
|
|||
|
27fdcfbae7 | ||
|
c021b986f5 | ||
|
2b5f3e5f88 | ||
|
30bdb0582f | ||
e4b006afc0
|
|||
a21b1382a4
|
|||
3e3127adcf
|
|||
6aea7ca11f
|
|||
6b9e9b5843
|
|||
5747fa91ca
|
|||
1a73613cd1
|
|||
4ee61adb07
|
|||
80b402ee40
|
|||
c02bc2314a
|
|||
d177620784
|
|||
354d0e1961
|
|||
bc1f379650
|
|||
63512254da
|
|||
69c5003af1
|
|||
ac17f3ae55
|
|||
d469adc667
|
|||
2bb2218145
|
|||
aae8a27fcc
|
|||
3b31e2c255
|
|||
f111e7d53c
|
|||
327f2139b8
|
|||
adb1f174ad
|
|||
373d5c6cb8
|
|||
aba5e3c23c
|
|||
8a2f0aca83
|
|||
a0a4d08612
|
|||
25747b2041
|
|||
8de1abf679
|
|||
b97de07843
|
|||
b497189549
|
|||
1ca659dd6a
|
|||
117b37b5cf
|
|||
967186bd00
|
|||
eea7b6d897
|
|||
dd75a13970
|
|||
66ed43f5ad
|
|||
399cfafd29 | |||
6ae1d3e726 | |||
384e6b657e | |||
08b7feeede | |||
e7fc0bbcd0 | |||
1f8022fd7c | |||
|
b404543f47 | ||
|
ad195d93ea | ||
15e7490808 | |||
7c906cd2a0 | |||
0da5875d64 | |||
947ec809e7 | |||
c07f3e99a1 | |||
ebba4d2345 | |||
e60fc6ae7c | |||
7688dbb5ca | |||
e703237a6d | |||
01494e8467 | |||
e08081ae42 | |||
758f80d1c1 | |||
b06ecaa5e0 | |||
df5090faac | |||
ffb706864a | |||
70eadb83e8 | |||
6d7d7290e2 | |||
1191c77986 | |||
2f4539d8f6 | |||
030a1590ae | |||
52820c0b07 | |||
1f2b1d5046 | |||
7896e4836c | |||
d2ff1fe3ec | |||
f5722e3e5c | |||
44e99cb3da | |||
1386833c91 | |||
69af0172eb | |||
81e66d2da2 | |||
73a9142e71 | |||
5af6ffb614 | |||
230b3b4e38 |
33
.editorconfig
Normal file
33
.editorconfig
Normal file
@@ -0,0 +1,33 @@
|
||||
# noinspection EditorConfigKeyCorrectness
|
||||
[{*.kt,*.kts}]
|
||||
ktlint_standard_annotation = disabled
|
||||
ktlint_standard_filename = disabled
|
||||
ktlint_standard_wrapping = disabled
|
||||
ktlint_standard_import-ordering = enabled
|
||||
ktlint_standard_max-line-length = disabled
|
||||
ktlint_standard_multiline-if-else = disabled
|
||||
ktlint_standard_argument-list-wrapping = disabled
|
||||
ktlint_standard_parameter-list-wrapping = disabled
|
||||
ktlint_standard_trailing-comma-on-declaration-site = disabled
|
||||
ktlint_function_signature_body_expression_wrapping = multiline
|
||||
ktlint_standard_string-template-indent = disabled
|
||||
ktlint_standard_function-signature = disabled
|
||||
ktlint_standard_trailing-comma-on-call-site = disabled
|
||||
ktlint_standard_multiline-expression-wrapping = disabled
|
||||
ktlint_standard_no-empty-first-line-in-class-body = disabled
|
||||
ktlint_standard_if-else-wrapping = disabled
|
||||
ktlint_standard_if-else-bracing = disabled
|
||||
ktlint_standard_statement-wrapping = disabled
|
||||
ktlint_standard_blank-line-before-declaration = disabled
|
||||
ktlint_standard_no-empty-file = disabled
|
||||
ktlint_standard_property-naming = disabled
|
||||
ktlint_standard_function-naming = disabled
|
||||
ktlint_standard_chain-method-continuation = disabled
|
||||
ktlint_standard_class-signature = disabled
|
||||
ktlint_standard_condition-wrapping = disabled
|
||||
ktlint_standard_class-signature = disabled
|
||||
ij_continuation_indent_size = 2
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
max_line_length = 150
|
29
.github/ISSUE_TEMPLATE/----------.md
vendored
29
.github/ISSUE_TEMPLATE/----------.md
vendored
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: 通知优化图标适配反馈
|
||||
about: 提交通知图标优化适配必须使用此模板提交
|
||||
title: "[通知优化图标适配反馈]"
|
||||
labels: To be adapted
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**需要适配的 APP 名称/包名/通知图标颜色 (必填)**
|
||||
|
||||
* (示例:阅读/com.heytap.reader/#fff2a135)
|
||||
|
||||
*
|
||||
|
||||
**提供相关 APP 的下载渠道截图以及简要说明用途 (必填)**
|
||||
|
||||
*
|
||||
|
||||
**提供相关 APP 的通知单色图标适配素材 大小 50x50 (选填)**
|
||||
|
||||
* (可填写资源下载地址或直接添加附件提交,不接受百度网盘、天翼云盘以及各种快传、私有云盘)
|
||||
|
||||
* (若直接在附件提交这里可不填)
|
||||
|
||||
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
|
||||
<!--- 请保留模板原始标题 --->
|
||||
<!--- 不按规定提交的 issues 将直接被关闭 --->
|
||||
<!--- Create by Template --->
|
52
.github/ISSUE_TEMPLATE/----bug---.md
vendored
52
.github/ISSUE_TEMPLATE/----bug---.md
vendored
@@ -1,52 +0,0 @@
|
||||
---
|
||||
name: 问题与 BUG 反馈
|
||||
about: 问题反馈必须使用此模板进行提交
|
||||
title: "[问题与 BUG 反馈] *简要描述问题原因*"
|
||||
labels: bug
|
||||
assignees: fankes
|
||||
|
||||
---
|
||||
|
||||
**系统版本(必填)**
|
||||
|
||||
*
|
||||
|
||||
**系统类型(请保留一个)**
|
||||
|
||||
* ColorOS/RealmeUI/OxygenOS
|
||||
|
||||
**Android 版本(必填)**
|
||||
|
||||
*
|
||||
|
||||
**模块版本(必填)**
|
||||
|
||||
*
|
||||
|
||||
**使用的 Xposed 框架名称与框架版本(必填)**
|
||||
|
||||
* (例如:LSPosed Zygisk/Riru 版本号)
|
||||
|
||||
**同时使用的带有系统界面作用域的 Xposed 模块(选填)**
|
||||
|
||||
* (没有可空)
|
||||
|
||||
**问题的具体描述**
|
||||
|
||||
* (复现步骤、前提以及详细截图和录屏演示)
|
||||
|
||||
|
||||
**提供模块问题 Log 或必要 Log**
|
||||
|
||||
* (LSPosed 可在日志管理中查看并筛选包含 `ColorOSNotifyIcon` 的日志)
|
||||
|
||||
<details><summary>展开查看</summary><pre><code>
|
||||
|
||||
(此处粘贴问题Log)
|
||||
|
||||
</code></pre></details>
|
||||
|
||||
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
|
||||
<!--- 请保留模板原始标题 --->
|
||||
<!--- 不按规定提交的 issues 将直接被关闭 --->
|
||||
<!--- Create by Template --->
|
93
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
93
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
name: 问题与 BUG 反馈
|
||||
description: 问题反馈必须使用此模板进行提交
|
||||
labels: [bug]
|
||||
title: "[问题与 BUG 反馈] (在这里简要描述问题原因)"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### 请在下方填写问题发生的具体原因和复现步骤。
|
||||
|
||||
RealmeUI 与 OxygenOS ≥ 12 都是基于 ColorOS 进行修改而来,理论功能是通用的,请额外注意在下方选择正确的系统类型。
|
||||
|
||||
我们只接受 ColorOS/RealmeUI/OxygenOS 正规官方版本系统,如果你正在使用官改(第三方修改版)请不要提交任何 BUG 与问题,我们无义务去解决,请自求多福。
|
||||
|
||||
发生异常、崩溃、闪退或功能性问题,必须提交问题 Log (日志),没有 Log 的 issues 将直接被关闭。
|
||||
- type: input
|
||||
attributes:
|
||||
label: 模块版本
|
||||
description: 请填写当前使用的模块完整版本号,例如:**2.0**
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: 系统类型
|
||||
description: 请选择你使用的系统类型。
|
||||
options:
|
||||
- ColorOS
|
||||
- RealmeUI
|
||||
- OxygenOS
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: 系统版本
|
||||
description: 这里填写当前的系统版本,以 ColorOS 举例:**ColorOS 12.1 LE2120_11_C.67**
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Android 版本
|
||||
options:
|
||||
- 15
|
||||
- 14
|
||||
- 13
|
||||
- 12L/12.1
|
||||
- 12
|
||||
- 11
|
||||
- 10
|
||||
- 9
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Xposed 框架名称与版本号
|
||||
description: 请填写当前使用的 Xposed 框架,例如:**LSPosed 1.8.4(次版本号)**
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: 与系统界面(系统 UI)同作用域的 Xposed 模块
|
||||
description: |
|
||||
此模块的作用域为系统界面(系统 UI),为确保非其它模块冲突造成的问题,请一定要填写当前你同时激活的相关模块。
|
||||
若没有,请直接在下方填写“无”。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 详细描述问题发生的具体原因
|
||||
description: 请在下方详细描述问题发生的具体场景、复现步骤和经过,以便我们能够按照你所描述的步骤复现这个问题。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 提供模块问题 Log 或必要 Log
|
||||
description: LSPosed 可在日志管理中查看并筛选包含 `ColorOSNotifyIcon` 的日志。
|
||||
value: |
|
||||
<details><summary>展开查看</summary><pre><code>
|
||||
|
||||
(此处粘贴问题 Log)
|
||||
|
||||
</code></pre></details>
|
||||
<!-- 提交时请将括号内容包括括号全部删除,粘贴你复制的日志,不要破坏代码格式 -->
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 确认一下你提交的信息
|
||||
description: |
|
||||
为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。
|
||||
请一定确保你已经**勾选下方的选项**后再提交。
|
||||
options:
|
||||
- label: 我确保上述信息准确无误
|
||||
required: false
|
71
.github/ISSUE_TEMPLATE/request_notify_icon_adaption.yml
vendored
Normal file
71
.github/ISSUE_TEMPLATE/request_notify_icon_adaption.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: 通知图标优化适配反馈
|
||||
description: 提交通知图标优化适配必须使用此模板提交
|
||||
labels: [To be adapted]
|
||||
title: "[通知图标优化适配反馈]"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### 请在下方填写你需要适配的 APP 通知图标的必要信息。
|
||||
|
||||
**China and Chinese only.**
|
||||
**仅限中国和中文。**
|
||||
|
||||
以下类型的 APP 不予适配:
|
||||
|
||||
- VPN、翻墙软件
|
||||
- 涉嫌色情、赌博类软件
|
||||
- 申请超限权限、涉嫌泄露国家机密行为的软件
|
||||
|
||||
以下类型的 APP 通知图标暂不适配:
|
||||
|
||||
- 多态彩色图标,状态不唯一,例如 360 极速浏览器
|
||||
- 规范的原生图标,但未被通知图标规则适配的 (将稍后加入白名单)
|
||||
- type: input
|
||||
attributes:
|
||||
label: APP 名称
|
||||
description: 这里填写 APP 的名称,例如:**微信**
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: APP 包名
|
||||
description: 这里填写 APP 的包名,例如:**com.tencent.mm**
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: 通知图标颜色 (HEX)
|
||||
description: |
|
||||
这里填写通知图标在下拉通知栏中的图标颜色,要求为 16 进制,例如:**#ff232323**
|
||||
如果不知道什么是 16 进制颜色,可以参考 [这里](https://www.qtccolor.com/tool/hex.aspx) 的取色。
|
||||
留空代表使用系统默认主题色。
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: 下载渠道、来源地址链接
|
||||
description: 请填写我们应该从何处得到你需要适配的这个 APP 的下载链接。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 简单描述适配的通知图标使用场景
|
||||
description: 简单描述一下当前 APP 的通知图标在何时会变成彩色的、不规范的以及可触发推送通知的操作,例如小米推送或 HMS 推送。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 通知单色图标适配素材 (大小 50x50~72x72)
|
||||
description: 请在这里填写我们能够获得这个图标的网址或在下方的文本框粘贴你需要上传的图标。
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 确认一下你提交的信息
|
||||
description: |
|
||||
为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。
|
||||
请一定确保你已经**勾选下方的选项**后再提交。
|
||||
options:
|
||||
- label: 我确保上述信息准确无误
|
||||
required: false
|
88
.github/workflows/commit_ci.yml
vendored
Normal file
88
.github/workflows/commit_ci.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: Automatic Build on Commit
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.txt'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build CI
|
||||
if: ${{ success() }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
APK_OUTPUT_PATH: 'app/build/outputs/apk'
|
||||
TG_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
TG_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
COMMIT_MESSAGE: |+
|
||||
New push to GitHub\!
|
||||
```
|
||||
${{ github.event.head_commit.message }}
|
||||
```by `${{ github.event.head_commit.author.name }}`
|
||||
See commit detail [here](${{ github.event.head_commit.url }})
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare GitHub Env
|
||||
run: |
|
||||
GITHUB_SHA=${{ github.sha }}
|
||||
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
|
||||
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1
|
||||
with:
|
||||
cmake-version: '3.22.1'
|
||||
- name: Prepare Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
java-package: jdk
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
- name: Cache Gradle Dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/build-cache-*
|
||||
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
|
||||
restore-keys: |
|
||||
gradle-deps
|
||||
- name: Cache Gradle Build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches/build-cache-*
|
||||
key: gradle-builds-core-${{ github.sha }}
|
||||
restore-keys: |
|
||||
gradle-builds
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew :app:assembleDebug
|
||||
./gradlew :app:assembleRelease
|
||||
echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts (Debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEBUG_APK_PATH }}
|
||||
name: ColorOSNotifyIcon-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.RELEASE_APK_PATH }}
|
||||
name: ColorOSNotifyIcon-release-${{ github.event.head_commit.id }}
|
||||
- name: Post Artifacts to Telegram
|
||||
run: |
|
||||
export debug=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name "*.apk")
|
||||
export release=$(find ${{ env.APK_OUTPUT_PATH }}/release -name "*.apk")
|
||||
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"])))'`
|
||||
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" \
|
||||
-F debug="@$debug" \
|
||||
-F release="@$release"
|
70
.github/workflows/pull_request_ci.yml
vendored
Normal file
70
.github/workflows/pull_request_ci.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Pull Request Checker
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.txt'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Pull Request Check
|
||||
if: ${{ success() }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
APK_OUTPUT_PATH: 'app/build/outputs/apk'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare GitHub Env
|
||||
run: |
|
||||
GITHUB_SHA=${{ github.sha }}
|
||||
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
|
||||
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1
|
||||
with:
|
||||
cmake-version: '3.22.1'
|
||||
- name: Prepare Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
java-package: jdk
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
- name: Cache Gradle Dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/build-cache-*
|
||||
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
|
||||
restore-keys: |
|
||||
gradle-deps
|
||||
- name: Cache Gradle Build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches/build-cache-*
|
||||
key: gradle-builds-core-${{ github.sha }}
|
||||
restore-keys: |
|
||||
gradle-builds
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew :app:assembleDebug
|
||||
./gradlew :app:assembleRelease
|
||||
echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts (Debug)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DEBUG_APK_PATH }}
|
||||
name: ColorOSNotifyIcon-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Release)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ env.RELEASE_APK_PATH }}
|
||||
name: ColorOSNotifyIcon-release-${{ github.event.head_commit.id }}
|
117
.gitignore
vendored
117
.gitignore
vendored
@@ -1,15 +1,110 @@
|
||||
## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects
|
||||
## References:
|
||||
## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
## - https://github.com/android/platform-samples/blob/main/.gitignore
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
.idea/.name
|
||||
.idea/artifacts
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/modules.xml
|
||||
.idea/*.iml
|
||||
.idea/modules
|
||||
.idea/caches
|
||||
.idea/material_theme**
|
||||
.idea/other.xml
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
*.ipr
|
||||
|
||||
# Kotlin
|
||||
.kotlin
|
||||
|
||||
# Misc
|
||||
.idea/misc.xml
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# Android studio 3.1+ additional
|
||||
.idea/deployment*.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/androidTestResultsUserPreferences.xml
|
||||
|
||||
# Android projects
|
||||
**/local.properties
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
|
||||
# Gradle projects
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Mkdocs temporary serving folder
|
||||
docs-gen
|
||||
site
|
||||
*.bak
|
||||
.idea/appInsightsSettings.xml
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
6
.idea/AndroidProjectSystem.xml
generated
Normal file
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
6
.idea/compiler.xml
generated
6
.idea/compiler.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
20
.idea/gradle.xml
generated
20
.idea/gradle.xml
generated
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="Embedded JDK" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
BIN
.idea/icon.png
generated
Normal file
BIN
.idea/icon.png
generated
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,10 +1,12 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
6
.idea/kotlinc.xml
generated
Normal file
6
.idea/kotlinc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.10" />
|
||||
</component>
|
||||
</project>
|
7
.idea/ktlint-plugin.xml
generated
Normal file
7
.idea/ktlint-plugin.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KtLint plugin">
|
||||
<ktlintMode>MANUAL</ktlintMode>
|
||||
<formatOnSave>false</formatOnSave>
|
||||
</component>
|
||||
</project>
|
6
.idea/ktlint.xml
generated
Normal file
6
.idea/ktlint.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KtlintProjectConfiguration">
|
||||
<treatAsErrors>false</treatAsErrors>
|
||||
</component>
|
||||
</project>
|
10
.idea/migrations.xml
generated
Normal file
10
.idea/migrations.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
28
.idea/misc.xml
generated
28
.idea/misc.xml
generated
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="app/src/main/res/drawable-night/bg_orange_round.xml" value="0.232" />
|
||||
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.254" />
|
||||
<entry key="app/src/main/res/drawable/bg_orange_round.xml" value="0.232" />
|
||||
<entry key="app/src/main/res/drawable/ic_nf_icon_refresh.xml" value="0.232" />
|
||||
<entry key="app/src/main/res/drawable/ic_nf_icon_update.xml" value="0.232" />
|
||||
<entry key="app/src/main/res/drawable/ic_notify_icon.xml" value="0.232" />
|
||||
<entry key="app/src/main/res/drawable/ic_system_clock.xml" value="0.232" />
|
||||
<entry key="app/src/main/res/layout/activity_config.xml" value="0.4375" />
|
||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.335" />
|
||||
<entry key="app/src/main/res/layout/adapter_config.xml" value="0.4375" />
|
||||
<entry key="app/src/main/res/layout/dia_icon_filter.xml" value="0.4375" />
|
||||
<entry key="app/src/main/res/layout/dia_source_from.xml" value="0.4375" />
|
||||
<entry key="app/src/main/res/layout/dia_source_from_string.xml" value="0.4375" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
17
.idea/runConfigurations.xml
generated
Normal file
17
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
359
PRIVACY.md
Normal file
359
PRIVACY.md
Normal file
@@ -0,0 +1,359 @@
|
||||
### ColorOS 通知图标增强 隐私政策
|
||||
|
||||
> 简体中文
|
||||
|
||||
版本:1.0
|
||||
|
||||
修订与生效日期:2022年4月25日
|
||||
|
||||
#### 前言
|
||||
|
||||
为帮助您使用 ColorOS 通知图标增强产品(以下简称“本产品”)或服务,我们可能收集与提供服务相关的设备信息或申请设备权限,您有权拒绝或撤回授权;
|
||||
|
||||
我们采取了互联网业内标准的技术措施和数据安全措施来保护您的设备信息安全;
|
||||
|
||||
除非再次征得您的同意,我们不会将您的设备信息用于本政策未载明的其他目的;
|
||||
|
||||
在阅读本协议前,请确认您已年满 18 岁,否则请在父母的陪同下阅读本协议。
|
||||
|
||||
#### 目录
|
||||
|
||||
```
|
||||
1. 引言
|
||||
2. 本产品处理设备信息的法律依据
|
||||
3. 我们如何收集和使用您的设备信息
|
||||
3.1 向您提供本产品和/或服务的核心业务功能
|
||||
3.2 征得授权同意的例外
|
||||
4. 我们如何转让、公开披露您的设备信息
|
||||
4.1 转让您的设备信息
|
||||
4.2 公开披露
|
||||
4.3 共享、转让、公开披露设备信息授权同意的例外
|
||||
5. 您管理设备信息的权利
|
||||
5.1 设备权限调用
|
||||
6. 如何更新与修改本政策
|
||||
7. 争议解决
|
||||
8. 名词解释
|
||||
```
|
||||
|
||||
1. 引言
|
||||
|
||||
【特别提示】
|
||||
|
||||
请您在使用我们的各项产品和/或服务前,仔细阅读并充分理解本政策。一旦您使用或继续使用本产品/服务,即表示您同意我们按照本政策处理您的相关信息。
|
||||
|
||||
2. 本产品处理设备信息的法律依据
|
||||
|
||||
如果您是中华人民共和国大陆地区的用户,我们将依据《中华人民共和国网络安全法》、《信息安全技术 个人信息安全规范》(GB/T 35273-2017)以及其他相关法律法规收集和使用您的设备信息或申请设备权限,为您提供本产品或服务。 我们通常只会在征得您同意的情况下收集您的设备信息。
|
||||
|
||||
3. 我们如何收集和使用您的设备信息
|
||||
|
||||
我们会遵循正当、合法、必要的原则,出于本政策所述的以下目的,收集和使用您在使用服务过程中主动提供或因使用本产品和/或服务而产生的设备信息。如果我们要将您的设备信息用于本政策未载明的其它用途,或基于特定目的将收集而来的信息用于其他目的,我们将以合理的方式向您告知,并在使用前再次征得您的同意。
|
||||
|
||||
3.1 向您提供本产品和/或服务的核心业务功能
|
||||
|
||||
为实现 本产品的核心业务功能,我们可能需要向您收集设备信息。以下将详细列出 本产品的核心业务功能及为实现该功能所需收集的设备信息,若您拒绝收集,则无法使用该服务。
|
||||
|
||||
查看设备中的所有应用信息。 本产品会读取您的设备应用列表;
|
||||
|
||||
搜索功能。 当您使用 本产品提供的搜索功能时,我们可能会使用应用数据缓存,收集您设备上的信息并进行本地存储。 该信息通常无法单独识别您的个人身份。
|
||||
|
||||
3.2 征得授权同意的例外
|
||||
|
||||
根据相关法律法规的规定,在以下情形中,我们可以在不征得您的授权同意的情况下收集、使用一些必要的设备信息:
|
||||
|
||||
```
|
||||
a. 与国家安全、国防安全直接相关的;
|
||||
b. 与公共安全、公共卫生、重大公共利益直接相关的;
|
||||
c. 与犯罪侦查、起诉、审判和判决执行等直接相关的;
|
||||
d. 出于维护您或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
|
||||
e. 所收集的设备信息是您自行向社会公众公开的;
|
||||
f. 从合法公开披露的信息中收集到您的设备信息,如从合法的新闻报道、政府信息公开等渠道;
|
||||
g. 根据您的要求签订和履行合同所必需的;
|
||||
h. 用于维护 本产品的产品和/或服务的安全稳定运行所必需的,例如发现、处置产品或服务的故障;
|
||||
i. 法律法规规定的其他情形。
|
||||
```
|
||||
|
||||
4. 我们如何共享、转让、公开披露您的设备信息
|
||||
|
||||
4.1 转让您的设备信息
|
||||
|
||||
除非获取您的明确同意,我们不会将您的设备信息转让给任何公司、组织或个人。
|
||||
|
||||
4.2 公开披露
|
||||
|
||||
除非获取您的明确同意,我们不会公开披露您的设备信息。
|
||||
|
||||
4.3 共享、转让、公开披露设备信息授权同意的例外
|
||||
|
||||
根据相关法律法规的规定,在以下情形中,我们可以在不征得您的授权同意的情况下共享、转让、公开披露您的设备信息:
|
||||
|
||||
```
|
||||
A. 与国家安全、国防安全有关的;
|
||||
B. 与公共安全、公共卫生、重大公共利益有关的;
|
||||
C. 与犯罪侦查、起诉、审判和判决执行等有关的;
|
||||
D. 出于维护您或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
|
||||
E. 您自行向社会公众公开的设备信息;
|
||||
F. 从合法公开披露的信息中收集到的设备信息的,如合法的新闻报道、政府信息公开等渠道。
|
||||
G. 法律法规规定的其他情形。
|
||||
```
|
||||
|
||||
根据法律规定,共享、转让经去标识化处理的设备信息,且确保数据接收方无法复原并重新识别设备信息主体的,不属于设备信息的对外共享、转让及公开披露行为,对此类数据的保存及处理将无需另行向您通知并征得您的同意。
|
||||
|
||||
5. 您管理设备信息的权利
|
||||
|
||||
5.1 设备权限调用
|
||||
|
||||
我们在提供服务的过程中,可能需要您开通一些设备权限,例如网络连接、查看设备应用列表等访问权限。 您也可以在设备的【设置】功能中随时选择关闭部分或者全部权限,从而拒绝我们收集您相应的设备信息。在不同设备中,权限显示方式及关闭方式可能有所不同,具体请参考设备及系统开发方说明或指引。
|
||||
|
||||
6. 如何更新与修改本政策
|
||||
|
||||
6.1 本政策为本产品的重要组成部分。 本产品保留不时更新或修改本政策的权利。
|
||||
|
||||
6.2 未经您明确同意,我们不会削减您按照本政策所应享有的权利。我们会通过 App 客户端推送通知、弹窗形式等合理方式通知您,以便您能及时了解本政策所做的任何变更。
|
||||
|
||||
6.3 对于重大变更,视具体情况我们 可能还会提供更为显著的通知 说明本政策的具体变更内容。 重大变更包括但不限于:
|
||||
|
||||
```
|
||||
A. 我们的服务模式发生重大变化。如处理设备信息的目的、处理的设备信息类型、设备信息的使用方式等;
|
||||
B. 我们在所有权结构、组织架构等方面发生重大变化。如业务调整、破产并购等引起的所有者变更等;
|
||||
C. 设备信息共享、转让或公开披露的主要对象发生变化;
|
||||
D. 您参与设备信息处理方面的权利及其行使方式发生重大变化;
|
||||
E. 我们负责处理设备信息安全的责任部门、联络方式及投诉渠道发生变化;
|
||||
F. 设备信息安全影响评估报告表明存在高风险时。
|
||||
```
|
||||
|
||||
6.4 若您不同意修改后的隐私政策,您有权并应立即停止使用 本产品的服务。如果您继续使用 本产品的服务,则视为您接受 本产品对本政策相关条款所做的修改。
|
||||
|
||||
7. 争议解决
|
||||
|
||||
7.1 如果您认为我们的设备信息处理行为损害了您的合法权益,您也可向有关政府部门进行反映。
|
||||
|
||||
8. 名词解释
|
||||
|
||||
本隐私政策中使用到的名词,在通常意义中有如下定义:
|
||||
|
||||
设备信息: 设备信息是指以电子或者其他方式记录的能够单独或者与其他信息结合识别设备身份的各种信息,包括但不限于设备的品牌、型号、系统版本、 唯一设备标识符、 网络 IP 地址、Cookie等;
|
||||
|
||||
设备: 设备是指可用于访问本产品和/或服务的装置,例如台式计算机、笔记本电脑、平板电脑或智能手机。 唯一设备标识符: 唯一设备标识符(专属 ID 或 UUID)是指由设备制造商编入到设备中的一串字符,可用于以独有方式标识相应设备(例如手机的 IMEI
|
||||
号)。唯一设备标识符有多种用途,其中可在不能使用 Cookie(例如在移动应用程序中)时用以提供广告。
|
||||
|
||||
IP 地址: 每台上网的设备都会指定一个编号,称为互联网协议 ( IP ) 地址。这些编号通常都是根据地理区域指定的。IP 地址通常可用于识别设备连接至互联网时所在的位置。
|
||||
|
||||
信息收集技术: 我们在《ColorOS 通知图标增强 隐私政策》中所述的“自动收集”包括以下方式: A. Cookie(曲奇档案) Cookie 是您浏览网页时,网站服务器放在客户端(您的计算机、移动电话或其他智能终端内)里面的一个小小的文本文件,当您再次访问相应网站时,网站就可通过
|
||||
Cookie 识别您的浏览器。Cookie 可能会存储用户偏好及其他信息。您可以将浏览器配置为拒绝所有 Cookie 或在网站发送 Cookie 时显示提示。不过,如果没有 Cookie,某些网站功能或服务可能无法正常工作。 B. Web beacon(网络信标) Web beacon
|
||||
是装嵌在网站或电邮内的电子图像文件案或其他技术,可用于计算访客数目、记录您是否及何时阅览电邮或网站,或用以使用某些 Cookie。 C. Log files(日志文件) Log files 储存自动收集的若干数据。该等数据包括互联网协议( IP )地址、浏览器类型、互联网服务提供商(
|
||||
ISP )、引用/退出页面、操作系统、日期/时间戳和点击流数据等。 D. ET Tag(实体标签) ET Tag 是在互联网浏览器与互联网服务器之间背后传送的 HTTP 协议标头,可代替 Cookie,用以追踪个别使用者,使我们可更深入地了解和改善我们的服务。 E. JavaScript
|
||||
JavaScript 是一种编程语言,用于制作更具互动性和动态的网页。JavaScript 可以设定 Cookie、阅读 Cookie 及删除 Cookie。
|
||||
|
||||
算法: 计算机在执行解题运算时遵循的流程或一系列规则。
|
||||
|
||||
应用数据缓存: 应用数据缓存是指设备上的一种数据存储机制。使用它有很多好处,例如,可让网络应用在未连接互联网的情况下运行,以及可通过提高内容加载速度来改善相关应用的性能。
|
||||
|
||||
非个人身份信息: 记录的与用户相关的信息,但实际上不可直接或间接识别您身份的信息,包括经过去标识化、匿名化处理或化名方式提供的设备信息。
|
||||
|
||||
去标识化: 指通过对设备信息的技术处理,使其在不借助额外信息的情况下,无法识别设备信息主体的过程。
|
||||
|
||||
匿名化: 指通过对设备信息的技术处理,使得设备信息主体无法被识别,且处理后的信息不能被复原的过程。
|
||||
|
||||
服务器日志: 与大多数网站一样,我们的服务器会自动记录您在访问网站时所发出的网页请求。这些“服务器日志”通常包括您的网络请求、互联网协议地址、浏览器类型、浏览器语言、请求的日期和时间及可以唯一识别您的浏览器的一个或多个 Cookie。
|
||||
|
||||
### ColorOSNotifyIcon Privacy Policy
|
||||
|
||||
> English
|
||||
|
||||
Version: 1.0
|
||||
|
||||
Amendment and effective date: April 25, 2022
|
||||
|
||||
#### Preface
|
||||
|
||||
In order to help you use ColorOSNotifyIcon products (hereinafter referred to as "this product") or services, we may collect device information
|
||||
related to the provision of services or apply for device permissions, and you have the right to refuse or withdraw authorization;
|
||||
|
||||
We have adopted technical measures and data security measures that are standard in the Internet industry to protect the security of your device
|
||||
information;
|
||||
|
||||
Unless we obtain your consent again, we will not use your device information for other purposes not specified in this policy;
|
||||
|
||||
Before reading this agreement, please confirm that you are at least 18 years old, otherwise please read this agreement with your parents.
|
||||
|
||||
#### content
|
||||
|
||||
````
|
||||
1 Introduction
|
||||
2. Legal basis for this product to process device information
|
||||
3. How we collect and use your device information
|
||||
3.1 Provide you with the core business functions of this product and/or service
|
||||
3.2 Exceptions to Authorized Consent
|
||||
4. How we transfer and publicly disclose your device information
|
||||
4.1 Transfer your device information
|
||||
4.2 Public disclosure
|
||||
4.3 Exceptions to Authorization and Consent for Sharing, Transfer, and Public Disclosure of Device Information
|
||||
5. Your Right to Manage Device Information
|
||||
5.1 Device permission call
|
||||
6. How to update and modify this policy
|
||||
7. Dispute Resolution
|
||||
8. Glossary
|
||||
````
|
||||
|
||||
1 Introduction
|
||||
|
||||
【Special Note】
|
||||
|
||||
Please read and fully understand this policy before using our products and/or services. Once you use or continue to use this product/service, you
|
||||
agree that we will process your relevant information in accordance with this policy.
|
||||
|
||||
2. Legal basis for this product to process device information
|
||||
|
||||
If you are a user in the mainland of the People's Republic of China, we will collect and use your Device information or apply for device
|
||||
permissions to provide you with this product or service. We generally only collect your device information with your consent.
|
||||
|
||||
3. How we collect and use your device information
|
||||
|
||||
We will follow the principles of legitimacy, lawfulness, and necessity, and for the following purposes described in this policy, to collect and
|
||||
use the device information that you actively provide during the use of the service or that arises from the use of this product and/or service. If
|
||||
we want to use your device information for other purposes not specified in this policy, or use the collected information for other purposes based
|
||||
on a specific purpose, we will inform you in a reasonable manner and ask again before use with your consent.
|
||||
|
||||
3.1 Provide you with the core business functions of this product and/or service
|
||||
|
||||
In order to implement the core business functions of this product, we may need to collect device information from you. The following will list in
|
||||
detail the core business functions of this product and the device information that needs to be collected to achieve this function. If you refuse
|
||||
to collect, you cannot use the service.
|
||||
|
||||
View all app information on the device. This product will read your device application list;
|
||||
|
||||
searching feature. When you use the search function provided by this product, we may use application data cache, collect information on your
|
||||
device and store it locally. This information generally does not individually identify you as an individual.
|
||||
|
||||
3.2 Exceptions to Authorized Consent
|
||||
|
||||
According to relevant laws and regulations, we may collect and use some necessary device information without your authorization and consent under
|
||||
the following circumstances:
|
||||
|
||||
````
|
||||
a. Directly related to national security and national defense security;
|
||||
b. Directly related to public safety, public health, and major public interests;
|
||||
c. Directly related to criminal investigation, prosecution, trial and execution of judgments;
|
||||
d. In order to protect your or other personal life, property and other major legitimate rights and interests, but it is difficult to obtain my consent;
|
||||
e. The collected device information is disclosed to the public by you;
|
||||
f. Your device information is collected from legally publicly disclosed information, such as legal news reports, government information disclosure and other channels;
|
||||
g. Necessary to enter into and perform a contract at your request;
|
||||
h. Necessary to maintain the safe and stable operation of the products and/or services of this product, such as finding and disposing of faults in the products or services;
|
||||
i. Other situations stipulated by laws and regulations.
|
||||
````
|
||||
|
||||
4. How we share, transfer, and publicly disclose your device information
|
||||
|
||||
4.1 Transfer your device information
|
||||
|
||||
We will not transfer your device information to any company, organization or individual without your express consent.
|
||||
|
||||
4.2 Public disclosure
|
||||
|
||||
We will not publicly disclose your device information without your express consent.
|
||||
|
||||
4.3 Exceptions to Authorization and Consent for Sharing, Transfer, and Public Disclosure of Device Information
|
||||
|
||||
According to relevant laws and regulations, we may share, transfer and publicly disclose your device information without your authorization and
|
||||
consent in the following circumstances:
|
||||
|
||||
````
|
||||
A. Related to national security and national defense security;
|
||||
B. Related to public safety, public health, and major public interests;
|
||||
C. Related to criminal investigation, prosecution, trial and execution of judgments;
|
||||
D. In order to protect your or other personal life, property and other major legitimate rights and interests, but it is difficult to obtain my consent;
|
||||
E. The device information that you disclose to the public by yourself;
|
||||
F. Device information collected from legally publicly disclosed information, such as legal news reports, government information disclosure and other channels.
|
||||
G. Other situations stipulated by laws and regulations.
|
||||
````
|
||||
|
||||
According to the law, sharing and transferring de-identified device information and ensuring that the data recipient cannot restore and
|
||||
re-identify the subject of the device information is not an act of external sharing, transfer and public disclosure of device information.
|
||||
Storage and processing will not be required to notify you and obtain your consent.
|
||||
|
||||
5. Your Right to Manage Device Information
|
||||
|
||||
5.1 Device permission call
|
||||
|
||||
In the process of providing services, we may require you to activate some device permissions, such as network connection, viewing device
|
||||
application list and other access permissions. You can also choose to turn off some or all permissions at any time in the [Settings] function of
|
||||
the device, thereby refusing us to collect your corresponding device information. In different devices, the display method and closing method of
|
||||
permissions may be different. For details, please refer to the instructions or guidelines of the device and system developers.
|
||||
|
||||
6. How to update and modify this policy
|
||||
|
||||
6.1 This policy is an important part of this product. This product reserves the right to update or modify this policy from time to time.
|
||||
|
||||
6.2 We will not reduce your rights under this Policy without your express consent. We will notify you through reasonable means such as app client
|
||||
push notifications and pop-up windows, so that you can keep abreast of any changes to this policy.
|
||||
|
||||
6.3 For major changes, we may also provide a more prominent notice to explain the specific changes to this policy as the case may be. Significant
|
||||
changes include, but are not limited to:
|
||||
|
||||
````
|
||||
A. Significant changes to our service model. Such as the purpose of processing device information, the type of device information processed, the way of using device information, etc.;
|
||||
B. We have made significant changes in our ownership structure, organizational structure, etc. Such as changes in owners caused by business adjustments, bankruptcy mergers and acquisitions, etc.;
|
||||
C. The main object of equipment information sharing, transfer or public disclosure has changed;
|
||||
D. Significant changes in your rights to participate in the processing of device information and the way you exercise it;
|
||||
E. The responsible department, contact information and complaint channel that we are responsible for handling equipment information security have changed;
|
||||
F. When the equipment information security impact assessment report indicates that there is a high risk.
|
||||
````
|
||||
|
||||
6.4 If you do not agree with the revised Privacy Policy, you have the right and should immediately stop using the services of this product. If
|
||||
you continue to use the services of this product, it is deemed that you accept the modifications made by this product to the relevant terms of
|
||||
this policy.
|
||||
|
||||
7. Dispute Resolution
|
||||
|
||||
7.1 If you believe that our device information processing has damaged your legitimate rights and interests, you can also report to the relevant
|
||||
government departments.
|
||||
|
||||
8. Glossary
|
||||
|
||||
Terms used in this Privacy Policy have the following definitions in their usual meanings:
|
||||
|
||||
Device information: Device information refers to various information recorded electronically or in other ways that can identify the identity of
|
||||
the device alone or in combination with other information, including but not limited to the brand, model, system version, unique device
|
||||
identifier, network IP address of the device , Cookies, etc.;
|
||||
|
||||
Device: A device is a device that can be used to access the Product and/or Service, such as a desktop computer, laptop, tablet or smartphone.
|
||||
Unique Device Identifier: A Unique Device Identifier (Unique ID or UUID) is a string of characters programmed into a device by the device
|
||||
manufacturer that can be used to uniquely identify the device (such as the IMEI of a mobile phone)
|
||||
No). Unique device identifiers are used for a variety of purposes, including serving advertisements when cookies cannot be used, such as in
|
||||
mobile applications.
|
||||
|
||||
IP Address: Every device that goes online is assigned a number called an Internet Protocol (IP) address. These numbers are usually assigned by
|
||||
geographic area. An IP address can often be used to identify where a device is connected to the Internet.
|
||||
|
||||
Information collection technology: The "automatic collection" described in the "ColorOSNotifyIcon Privacy Policy" includes the following methods:
|
||||
A. Cookie (cookie file) Cookie is when you browse the web, the website server places it on the client (your A small text file in a computer,
|
||||
mobile phone or other intelligent terminal), when you visit the corresponding website again, the website can pass Cookies identify your browser.
|
||||
Cookies may store user preferences and other information. You can configure your browser to refuse all cookies or to display a prompt when a
|
||||
website sends a cookie. However, some website features or services may not function properly without cookies. B. Web beacon (web beacon)
|
||||
Web beacon An electronic image file or other technology embedded in a website or email that can be used to count visitors, record whether and
|
||||
when you view an email or website, or to use certain cookies. C. Log files Log files store certain data collected automatically. This data
|
||||
includes Internet Protocol (IP) address, browser type, Internet Service Provider (
|
||||
ISP), referring/exit pages, operating system, date/time stamp and clickstream data, etc. D. ET Tag (Entity Tag) ET Tag is an HTTP protocol header
|
||||
transmitted behind the Internet browser and Internet server, which can replace cookies to track individual users, so that we can better
|
||||
understand and improve our Serve. E. JavaScript JavaScript is a programming language used to make more interactive and dynamic web pages.
|
||||
JavaScript can set cookies, read cookies and delete cookies.
|
||||
|
||||
Algorithm: A process or set of rules that a computer follows when performing a problem-solving operation.
|
||||
|
||||
App data cache: App data cache refers to a data storage mechanism on the device. There are many benefits to using it, such as allowing web
|
||||
applications to run without an internet connection, and improving the performance of related applications by increasing the speed of content
|
||||
loading.
|
||||
|
||||
Non-Personally Identifiable Information: Information that is recorded about a user but does not actually identify you directly or indirectly,
|
||||
including device information that has been de-identified, anonymized, or provided under a pseudonym.
|
||||
|
||||
De-identification: refers to the process of technical processing of device information to make it impossible to identify the subject of device
|
||||
information without the aid of additional information.
|
||||
|
||||
Anonymization: refers to the process in which the subject of the device information cannot be identified and the processed information cannot be
|
||||
recovered through the technical processing of the device information.
|
||||
|
||||
Server Logs: Like most websites, our servers automatically log the web page requests you make while visiting the website. These "server logs"
|
||||
typically include your web request, internet protocol address, browser type, browser language, date and time of the request, and one or more
|
||||
cookies that uniquely identify your browser.
|
126
README.md
126
README.md
@@ -1,51 +1,116 @@
|
||||
# ColorOS 通知图标增强
|
||||
|
||||
[](https://github.com/fankes/ColorOSNotifyIcon)
|
||||
[](https://github.com/fankes/ColorOSNotifyIcon/blob/master/LICENSE)
|
||||
[](https://github.com/fankes/ColorOSNotifyIcon/releases)
|
||||
[](https://github.com/fankes/ColorOSNotifyIcon/releases)
|
||||
[](https://github.com/Xposed-Modules-Repo/com.fankes.coloros.notify/releases)
|
||||
[](https://t.me/XiaofangInternet)
|
||||
<br/><br/>
|
||||
<img src="https://github.com/fankes/ColorOSNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
|
||||
<br/>
|
||||
Optimize notification icons for ColorOS and adapt to native notification icon specifications.<br/>
|
||||
[](https://github.com/fankes/ColorOSNotifyIcon/blob/master/LICENSE)
|
||||
[](https://github.com/fankes/ColorOSNotifyIcon/actions/workflows/commit_ci.yml)
|
||||
[](https://github.com/fankes/ColorOSNotifyIcon/releases)
|
||||

|
||||

|
||||
|
||||
[](https://t.me/ColorOSNotifyIcon_CI)
|
||||
[](https://t.me/XiaofangInternet)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM)
|
||||
[](https://pd.qq.com/s/44gcy28h)
|
||||
|
||||
<img src="img-src/icon.png" width = "100" height = "100" alt="LOGO"/>
|
||||
|
||||
Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
|
||||
为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeUI。
|
||||
|
||||
# Developer
|
||||
## For Non-Chinese Users
|
||||
|
||||
[酷安 @星夜不荟](http://www.coolapk.com/u/876977)
|
||||
This project will not be adapted i18n, please stay tuned for my new projects in the future.
|
||||
|
||||
# 适配说明
|
||||
## 项目迁移公告
|
||||
|
||||
由于本人同时维护 **MIUI** 与 **ColorOS** 两个系统需要同时维护两个模块,十分不方便,所以我决定在后期逐渐合并两个项目并解耦合为一个新项目并计划适配更多系统与设备,例如原生与类原生系统。
|
||||
|
||||
在新的项目确定后,会在这里添加新项目的链接,届时我会终止维护这个项目并建议大家转移到新项目。
|
||||
|
||||
## 适配说明
|
||||
|
||||
- 此模块仅支持 **LSPosed** (作用域“系统界面”)、**~~EdXposed(随时停止支持)~~**、不支持**太极、无极**
|
||||
|
||||
- 目前仅在 ColorOS 12、12.1、13 for OnePlus 上测试通过,如有问题请提交 `issues`
|
||||
|
||||
- 此模块仅支持 <b>LSPosed</b>(作用域“系统界面”)、<b>~~EdXposed(随时停止支持)~~</b>、不支持<b>太极、无极</b>
|
||||
- 目前仅在 ColorOS 12 for OnePlus 上测试通过,如有问题请提交 `issues`
|
||||
- 建议在不低于 ColorOS 11 的版本上使用
|
||||
|
||||
# 请勿用于非法用途
|
||||
## 注意事项
|
||||
|
||||
- 本模块完全开源免费,如果好用你可以打赏支持开发,但是请不要用于非法用途。
|
||||
- 本模块发布地址仅有 [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.coloros.notify/releases)、
|
||||
[Release](https://github.com/fankes/ColorOSNotifyIcon/releases)
|
||||
及 [蓝奏云](https://fankes.lanzouy.com/b030rvjyf),从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
|
||||
由于 ColorOS 15 版本的系统性通知图标行为变更,系统强制在通知图标初始化阶段就将图标强制替换为 APP 彩色图标进行破坏,所以目前加入了 “系统框架”
|
||||
作用域,如果在模块安装后没有自动勾选此作用域,请手动进行勾选并重新启动系统以修复此破坏行为,ColorOS 15 以下版本的系统无需勾选。
|
||||
|
||||
# 贡献通知图标优化名单
|
||||
感谢 [Nep-Timeline](https://github.com/Nep-Timeline) 提供的解决方案。
|
||||
|
||||
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。<br/>
|
||||
## 历史背景
|
||||
|
||||
继 MIUI 之后的第二大系统 ColorOS 虽然支持原生通知图标,但是第三方推送五颜六色的图标系统并没有做适配,甚至系统自己的图标都是彩色的,极其不友好。
|
||||
|
||||
而且从 ColorOS 12 开始,原生图标丢失了着色属性,这也是一种对原生 Android 生态的破坏。
|
||||
|
||||
## 贡献通知图标优化名单
|
||||
|
||||
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。
|
||||
|
||||
- [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
|
||||
|
||||
# 历史背景
|
||||
## 发行渠道
|
||||
|
||||
继 MIUI 之后的第二大系统 ColorOS 虽然支持原生通知图标,但是第三方推送五颜六色的图标系统并没有做适配,甚至系统自己的图标都是彩色的,极其不友好。<br/>
|
||||
而且从 ColorOS 12 开始,原生图标丢失了着色属性,这也是一种对原生 Android 生态的破坏。
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub CI](https://github.com/fankes/ColorOSNotifyIcon/actions/workflows/commit_ci.yml) | CI 自动构建 (测试版) |
|
||||
|------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|---------------|
|
||||
|
||||
# 许可证
|
||||
| <img src="https://github.com/peter-iakovlev/Telegram/blob/public/Icon.png?raw=true" width = "30" height = "30" alt="LOGO"/> | [Telegram CI 频道](https://t.me/ColorOSNotifyIcon_CI) | CI 自动构建 (测试版) |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|---------------|
|
||||
|
||||
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub Releases](https://github.com/fankes/ColorOSNotifyIcon/releases) | 正式版 (稳定版) |
|
||||
|------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|-----------|
|
||||
|
||||
| <img src="https://avatars.githubusercontent.com/u/78217009?s=200&v=4?raw=true" width = "30" height = "30" alt="LOGO"/> | [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.coloros.notify/releases) | 正式版 (稳定版) |
|
||||
|------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|-----------|
|
||||
|
||||
| <img src="https://github.com/fankes/fankes/assets/37344460/82113d3c-aa7b-4dd1-95c7-cda650065c12" width = "30" height = "30" alt="LOGO"/> | [123 云盘 **(密码:al5u)**](https://www.123pan.com/s/5SlUVv-C8DBh.html) | 正式版 (稳定版) |
|
||||
|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|-----------|
|
||||
|
||||
本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
|
||||
|
||||
## 注意事项
|
||||
|
||||
<h3>1. 本软件免费、由兴趣驱动开发,仅供学习交流使用。如果你是从其他非官方渠道付费获得本软件,可能已遭遇欺诈,欢迎向我们举报可疑行为。</h3>
|
||||
|
||||
<h3>2. 本软件采用 <strong>GNU Affero General Public License (AGPL 3.0)</strong> 许可证。根据该许可证的要求:</h3>
|
||||
|
||||
- 任何衍生作品必须采用相同的 AGPL 许可证
|
||||
- 分发本软件或其修改版本时,必须提供完整的源代码
|
||||
- 必须保留原始的版权声明及许可证信息
|
||||
- 不得额外施加限制来限制他人对本软件的自由使用
|
||||
|
||||
<h3>3. 我们鼓励在遵守 AGPL 3.0 条款的前提下进行自由传播和改进,但请尊重作者署名权,勿冒用原作者名义。</h3>
|
||||
|
||||
## 项目推广
|
||||
|
||||
<!--suppress HtmlDeprecatedAttribute -->
|
||||
<div align="center">
|
||||
<h2>嘿,还请君留步!👋</h2>
|
||||
<h3>这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。</h3>
|
||||
<h3>如果下方的项目能为你提供帮助,不妨为我点个 star 吧!</h3>
|
||||
<h3>所有项目免费、开源,遵循对应开源许可协议。</h3>
|
||||
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README-zh-CN.md">→ 查看更多关于我的项目,请点击这里 ←</a></h1>
|
||||
</div>
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
## 隐私政策
|
||||
|
||||
- [PRIVACY](PRIVACY.md)
|
||||
|
||||
## 许可证
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
```
|
||||
Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
@@ -58,8 +123,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
||||
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)<br/><br/>
|
||||
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
|
||||
|
||||
版权所有 © 20174 Fankes Studio(qzmmcn@163.com)
|
3
app/.gitignore
vendored
3
app/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/build
|
||||
/src/main/assets/xposed_init
|
||||
/src/main/resources/META-INF/yukihookapi_init
|
@@ -1,90 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'com.google.devtools.ksp' version '1.6.10-1.0.4'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 31
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('../keystore/public')
|
||||
storePassword '123456'
|
||||
keyAlias 'public'
|
||||
keyPassword '123456'
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.fankes.coloros.notify"
|
||||
minSdk 29
|
||||
targetSdk 31
|
||||
versionCode rootProject.ext.appVersionCode
|
||||
versionName rootProject.ext.appVersionName
|
||||
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = [
|
||||
'-Xno-param-assertions',
|
||||
'-Xno-call-assertions',
|
||||
'-Xno-receiver-assertions'
|
||||
]
|
||||
}
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
signingConfig signingConfigs.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除无效耗时 lint Task */
|
||||
tasks.whenTaskAdded {
|
||||
task -> if (task.name == "lintVitalRelease") task.enabled = false
|
||||
}
|
||||
|
||||
/** 移除无效耗时 lint Task */
|
||||
tasks.whenTaskAdded {
|
||||
task -> if (task.name == "lintVitalAnalyzeRelease") task.enabled = false
|
||||
}
|
||||
|
||||
/** 移除无效耗时 lint Task */
|
||||
tasks.whenTaskAdded {
|
||||
task -> if (task.name == "lintVitalReportRelease") task.enabled = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
implementation 'com.highcapable.yukihookapi:api:1.0.68'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.68'
|
||||
implementation 'com.github.tiann:FreeReflection:3.1.0'
|
||||
implementation "com.github.topjohnwu.libsu:core:3.1.2"
|
||||
implementation 'androidx.annotation:annotation:1.3.0'
|
||||
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
|
||||
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'com.google.android.material:material:1.5.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
}
|
89
app/build.gradle.kts
Normal file
89
app/build.gradle.kts
Normal file
@@ -0,0 +1,89 @@
|
||||
plugins {
|
||||
autowire(libs.plugins.android.application)
|
||||
autowire(libs.plugins.kotlin.android)
|
||||
autowire(libs.plugins.kotlin.ksp)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = property.project.app.packageName
|
||||
compileSdk = property.project.android.compileSdk
|
||||
|
||||
signingConfigs {
|
||||
create("universal") {
|
||||
keyAlias = property.project.app.signing.keyAlias
|
||||
keyPassword = property.project.app.signing.keyPassword
|
||||
storeFile = rootProject.file(property.project.app.signing.storeFilePath)
|
||||
storePassword = property.project.app.signing.storePassword
|
||||
enableV1Signing = true
|
||||
enableV2Signing = true
|
||||
}
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId = property.project.app.packageName
|
||||
minSdk = property.project.android.minSdk
|
||||
targetSdk = property.project.android.targetSdk
|
||||
versionName = property.project.app.versionName
|
||||
versionCode = property.project.app.versionCode
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
all { signingConfig = signingConfigs.getByName("universal") }
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
}
|
||||
lint { checkReleaseBuilds = false }
|
||||
androidResources.additionalParameters += listOf("--allow-reserved-package-id", "--package-id", "0x37")
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants(selector().all()) {
|
||||
it.outputs.forEach { output ->
|
||||
val currentType = it.buildType
|
||||
|
||||
// Workaround for GitHub Actions.
|
||||
// Why? I don't know, but it works.
|
||||
// Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
|
||||
// public inline fun CharSequence.isNotBlank(): Boolean defined in kotlin.text.
|
||||
@Suppress("UNNECESSARY_SAFE_CALL", "RemoveRedundantCallsOfConversionMethods")
|
||||
val currentSuffix = property.github.ci.commit.id?.let { suffix ->
|
||||
// Workaround for GitHub Actions.
|
||||
// Strongly transfer type to [String].
|
||||
val sSuffix = suffix.toString()
|
||||
if (sSuffix.isNotBlank()) "-$sSuffix" else ""
|
||||
}
|
||||
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
|
||||
if (output is com.android.build.api.variant.impl.VariantOutputImpl)
|
||||
output.outputFileName.set("${property.project.name}-v$currentVersion-$currentType.apk")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(de.robv.android.xposed.api)
|
||||
implementation(com.highcapable.yukihookapi.api)
|
||||
ksp(com.highcapable.yukihookapi.ksp.xposed)
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
implementation(com.fankes.projectpromote.project.promote)
|
||||
implementation(com.github.topjohnwu.libsu.core)
|
||||
implementation(com.github.duanhong169.drawabletoolbox)
|
||||
implementation(com.squareup.okhttp3.okhttp)
|
||||
implementation(androidx.core.core.ktx)
|
||||
implementation(androidx.appcompat.appcompat)
|
||||
implementation(com.google.android.material.material)
|
||||
implementation(androidx.constraintlayout.constraintlayout)
|
||||
testImplementation(junit.junit)
|
||||
androidTestImplementation(androidx.test.ext.junit)
|
||||
androidTestImplementation(androidx.test.espresso.espresso.core)
|
||||
}
|
16
app/proguard-rules.pro
vendored
16
app/proguard-rules.pro
vendored
@@ -20,7 +20,6 @@
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontwarn
|
||||
-ignorewarnings
|
||||
-optimizationpasses 10
|
||||
-dontusemixedcaseclassnames
|
||||
@@ -33,22 +32,15 @@
|
||||
-adaptresourcefilecontents
|
||||
|
||||
-renamesourcefileattribute P
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
-keep class me.weishu**{*;}
|
||||
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Application
|
||||
-keep public class * extends android.app.Service
|
||||
-keep public class * extends android.content.ContentProvider
|
||||
-keep public class * extends android.app.backup.BackupAgentHelper
|
||||
-keep public class * extends android.preference.Preference
|
||||
-keepattributes SourceFile,Signature,LineNumberTable
|
||||
|
||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||
public static *** throwUninitializedProperty(...);
|
||||
public static *** throwUninitializedPropertyAccessException(...);
|
||||
}
|
||||
|
||||
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
|
||||
-keep class * extends android.app.Activity
|
||||
-keep class * implements androidx.viewbinding.ViewBinding {
|
||||
<init>();
|
||||
*** inflate(android.view.LayoutInflater);
|
||||
}
|
@@ -1,10 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.fankes.coloros.notify">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
<permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
|
||||
<application
|
||||
android:name=".application.CNNApplication"
|
||||
|
@@ -1 +0,0 @@
|
||||
com.fankes.coloros.notify.hook.HookEntry_YukiHookXposedInit
|
@@ -1 +0,0 @@
|
||||
com.fankes.coloros.notify.hook.HookEntry
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,39 +18,23 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/24.
|
||||
* This file is created by fankes on 2022/1/24.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.coloros.notify.application
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import me.weishu.reflection.Reflection
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
|
||||
|
||||
class CNNApplication : Application() {
|
||||
|
||||
companion object {
|
||||
|
||||
/** 全局静态实例 */
|
||||
private var context: CNNApplication? = null
|
||||
|
||||
/** 调用全局静态实例 */
|
||||
val appContext get() = context ?: error("App is death")
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
super.attachBaseContext(base)
|
||||
/** 解锁隐藏 API */
|
||||
Reflection.unseal(base)
|
||||
}
|
||||
class CNNApplication : ModuleApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
/** 设置静态实例 */
|
||||
context = this
|
||||
/** 跟随系统夜间模式 */
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
/** 装载存储控制类 */
|
||||
ConfigData.init(instance = this)
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,7 +18,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/30.
|
||||
* This file is created by fankes on 2022/1/30.
|
||||
*/
|
||||
package com.fankes.coloros.notify.bean
|
||||
|
||||
@@ -47,13 +47,15 @@ data class IconDataBean(
|
||||
) : Serializable {
|
||||
fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
|
||||
fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all"
|
||||
override fun toString() = "{\n" +
|
||||
" \"appName\": \"$appName\",\n" +
|
||||
" \"packageName\": \"$packageName\",\n" +
|
||||
" \"iconBitmap\": \"${iconBitmap.base64}\",\n" +
|
||||
" \"iconColor\": \"#${Integer.toHexString(iconColor)}\",\n" +
|
||||
" \"contributorName\": \"$contributorName\",\n" +
|
||||
" \"isEnabled\": $isEnabled,\n" +
|
||||
" \"isEnabledAll\": $isEnabledAll\n" +
|
||||
" }"
|
||||
override fun toString() = """
|
||||
{
|
||||
"appName": "$appName",
|
||||
"packageName": "$packageName",
|
||||
"iconBitmap": "${iconBitmap.base64}",
|
||||
"iconColor": "#${Integer.toHexString(iconColor)}",
|
||||
"contributorName": "$contributorName",
|
||||
"isEnabled": $isEnabled,
|
||||
"isEnabledAll": $isEnabledAll
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/24.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.coloros.notify.const
|
||||
|
||||
import com.fankes.coloros.notify.BuildConfig
|
||||
|
||||
/**
|
||||
* 存储一些静态编译后的值
|
||||
*/
|
||||
object Const {
|
||||
|
||||
/** 当前模块的包名 */
|
||||
const val MODULE_PACKAGE_NAME = BuildConfig.APPLICATION_ID
|
||||
|
||||
/** 当前模块的版本名称 */
|
||||
const val MODULE_VERSION_NAME = BuildConfig.VERSION_NAME
|
||||
|
||||
/** 当前模块的版本号 */
|
||||
const val MODULE_VERSION_CODE = BuildConfig.VERSION_CODE
|
||||
|
||||
/** 当前模块的版本校验 */
|
||||
const val MODULE_VERSION_VERIFY = "${MODULE_VERSION_NAME}_${MODULE_VERSION_CODE}_202203292119"
|
||||
|
||||
/** 当前模块的版本校验标签 */
|
||||
const val MODULE_VERSION_VERIFY_TAG = "module_version_verify"
|
||||
|
||||
/** 发送通讯广播号标签 - 模块激活状态 */
|
||||
const val ACTION_MODULE_CHECKING_RECEIVER = "cnn_module_checking_action"
|
||||
|
||||
/** 接收通讯广播号标签 - 模块激活状态 */
|
||||
const val ACTION_MODULE_HANDLER_RECEIVER = "cnn_module_handler_action"
|
||||
|
||||
/** 发送通讯广播号标签 - 通知系统界面刷新 */
|
||||
const val ACTION_REMIND_CHECKING_RECEIVER = "cnn_remind_checking_action"
|
||||
|
||||
/** 接收通讯广播号标签 - 通知系统界面刷新 */
|
||||
const val ACTION_REMIND_HANDLER_RECEIVER = "cnn_remind_handler_action"
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/2/2.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.coloros.notify.const
|
||||
|
||||
import com.fankes.coloros.notify.generated.AppProperties
|
||||
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
|
||||
|
||||
/**
|
||||
* 包名常量定义类
|
||||
*/
|
||||
object PackageName {
|
||||
|
||||
/** 系统框架 */
|
||||
const val SYSTEM_FRAMEWORK = "android"
|
||||
|
||||
/** 系统界面 (系统 UI) */
|
||||
const val SYSTEMUI = "com.android.systemui"
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知图标优化名单同步方式定义类
|
||||
*/
|
||||
object IconRuleSourceSyncType {
|
||||
|
||||
/** GitHub Raw (代理 - GitHub Proxy) */
|
||||
const val GITHUB_RAW_PROXY_1 = 500
|
||||
|
||||
/** GitHub Raw (代理 - 7ED Services) */
|
||||
const val GITHUB_RAW_PROXY_2 = 1000
|
||||
|
||||
/** GitHub Raw (直连) */
|
||||
const val GITHUB_RAW_DIRECT = 2000
|
||||
|
||||
/** 自定义地址 */
|
||||
const val CUSTOM_URL = 3000
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块版本常量定义类
|
||||
*/
|
||||
object ModuleVersion {
|
||||
|
||||
/** 当前 GitHub 提交的 ID (CI 自动构建) */
|
||||
const val GITHUB_COMMIT_ID = AppProperties.GITHUB_CI_COMMIT_ID
|
||||
|
||||
/** 版本名称 */
|
||||
const val NAME = BuildConfigWrapper.VERSION_NAME
|
||||
|
||||
/** 版本号 */
|
||||
const val CODE = BuildConfigWrapper.VERSION_CODE
|
||||
|
||||
/** 是否为 CI 自动构建版本 */
|
||||
val isCiMode = GITHUB_COMMIT_ID.isNotBlank()
|
||||
|
||||
/** 当前版本名称后缀 */
|
||||
val suffix = GITHUB_COMMIT_ID.let { if (it.isNotBlank()) "-$it" else "" }
|
||||
|
||||
override fun toString() = "$NAME$suffix($CODE)"
|
||||
}
|
389
app/src/main/java/com/fankes/coloros/notify/data/ConfigData.kt
Normal file
389
app/src/main/java/com/fankes/coloros/notify/data/ConfigData.kt
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/2/2.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.coloros.notify.data
|
||||
|
||||
import android.content.Context
|
||||
import com.fankes.coloros.notify.const.IconRuleSourceSyncType
|
||||
import com.fankes.coloros.notify.utils.factory.isUpperOfAndroidS
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
|
||||
/**
|
||||
* 全局配置存储控制类
|
||||
*/
|
||||
object ConfigData {
|
||||
|
||||
/** 启用模块 */
|
||||
val ENABLE_MODULE = PrefsData("_enable_module", true)
|
||||
|
||||
/** 启用模块日志 */
|
||||
val ENABLE_MODULE_LOG = PrefsData("_enable_module_log", false)
|
||||
|
||||
/** 启用通知图标兼容模式 */
|
||||
val ENABLE_COLOR_ICON_COMPAT = PrefsData("_color_icon_compat", false)
|
||||
|
||||
/** 移除开发者选项警告通知 */
|
||||
val ENABLE_REMOVE_DEV_NOTIFY = PrefsData("_remove_dev_notify", true)
|
||||
|
||||
/** 移除充电完成通知 */
|
||||
val ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY = PrefsData("_remove_charge_complete_notify", false)
|
||||
|
||||
/** 移除免打扰通知 */
|
||||
val ENABLE_REMOVE_DND_ALERT_NOTIFY = PrefsData("_remove_dndalert_notify", false)
|
||||
|
||||
/** 启用 Material 3 通知图标风格 */
|
||||
val ENABLE_MD3_NOTIFY_ICON_STYLE = PrefsData("_notify_icon_md3_style", isUpperOfAndroidS)
|
||||
|
||||
/** 通知栏中的通知图标圆角程度 */
|
||||
val NOTIFY_ICON_CORNER_SIZE = PrefsData("_notify_icon_corner", 15)
|
||||
|
||||
/** 强制通知栏中的通知图标使用系统着色 */
|
||||
val ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR = PrefsData("_notify_icon_force_system_color", false)
|
||||
|
||||
/** 强制通知栏中的通知图标为 APP 图标 */
|
||||
val ENABLE_NOTIFY_ICON_FORCE_APP_ICON = PrefsData("_notify_icon_force_app_icon", false)
|
||||
|
||||
/** 启用媒体通知播放时自动展开 */
|
||||
val ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP = PrefsData("_enable_notify_media_panel_auto_exp", false)
|
||||
|
||||
/** 启用自定义通知面板背景透明度 */
|
||||
val ENABLE_NOTIFY_PANEL_ALPHA = PrefsData("_enable_notify_panel_alpha_pst", false)
|
||||
|
||||
/** 自定义通知面板背景透明度 */
|
||||
val NOTIFY_PANEL_ALPHA_LEVEL = PrefsData("_notify_panel_alpha_pst", 75)
|
||||
|
||||
/** 启用通知图标优化 */
|
||||
val ENABLE_NOTIFY_ICON_FIX = PrefsData("_notify_icon_fix", true)
|
||||
|
||||
/** 使用占位符修补未适配的通知图标 */
|
||||
val ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER = PrefsData("_notify_icon_fix_placeholder", false)
|
||||
|
||||
/** 提醒未适配通知图标的新安装应用 */
|
||||
val ENABLE_NOTIFY_ICON_FIX_NOTIFY = PrefsData("_notify_icon_fix_notify", true)
|
||||
|
||||
/** 启用通知图标优化名单自动更新 */
|
||||
val ENABLE_NOTIFY_ICON_FIX_AUTO = PrefsData("_enable_notify_icon_fix_auto", true)
|
||||
|
||||
/** 通知图标优化名单自动更新时间 */
|
||||
val NOTIFY_ICON_FIX_AUTO_TIME = PrefsData("_notify_icon_fix_auto_time", "07:00")
|
||||
|
||||
/** 通知图标优化适配数据 */
|
||||
val NOTIFY_ICONS_DATA = PrefsData("_notify_icon_datas", "")
|
||||
|
||||
/** 通知图标优化名单同步方式 */
|
||||
val ICON_RULE_SOURCE_SYNC_TYPE = PrefsData("_rule_source_sync_way", IconRuleSourceSyncType.GITHUB_RAW_PROXY_1)
|
||||
|
||||
/** 通知图标优化名单同步地址 */
|
||||
val ICON_RULE_SOURCE_SYNC_CUSTOM_URL = PrefsData("_rule_source_sync_way_custom_url", "")
|
||||
|
||||
/** 当前实例 - [Context] or [PackageParam] */
|
||||
private var instance: Any? = null
|
||||
|
||||
/**
|
||||
* 初始化存储控制类
|
||||
* @param instance 实例 - 只能是 [Context] or [PackageParam]
|
||||
* @throws IllegalStateException 如果类型错误
|
||||
*/
|
||||
fun init(instance: Any) {
|
||||
when (instance) {
|
||||
is Context, is PackageParam -> this.instance = instance
|
||||
else -> error("Unknown type for init ConfigData")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 [String] 数据
|
||||
* @param data 键值数据模板
|
||||
* @return [String]
|
||||
*/
|
||||
private fun getString(data: PrefsData<String>) = when (instance) {
|
||||
is Context -> (instance as Context).prefs().get(data)
|
||||
is PackageParam -> (instance as PackageParam).prefs.get(data)
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
|
||||
/**
|
||||
* 存入 [String] 数据
|
||||
* @param data 键值数据模板
|
||||
* @param value 键值内容
|
||||
*/
|
||||
private fun putString(data: PrefsData<String>, value: String) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).prefs().edit { put(data, value) }
|
||||
is PackageParam -> YLog.warn("Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 [Int] 数据
|
||||
* @param data 键值数据模板
|
||||
* @return [Int]
|
||||
*/
|
||||
internal fun getInt(data: PrefsData<Int>) = when (instance) {
|
||||
is Context -> (instance as Context).prefs().get(data)
|
||||
is PackageParam -> (instance as PackageParam).prefs.get(data)
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
|
||||
/**
|
||||
* 存入 [Int] 数据
|
||||
* @param data 键值数据模板
|
||||
* @param value 键值内容
|
||||
*/
|
||||
internal fun putInt(data: PrefsData<Int>, value: Int) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).prefs().edit { put(data, value) }
|
||||
is PackageParam -> YLog.warn("Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 [Boolean] 数据
|
||||
* @param data 键值数据模板
|
||||
* @return [Boolean]
|
||||
*/
|
||||
internal fun getBoolean(data: PrefsData<Boolean>) = when (instance) {
|
||||
is Context -> (instance as Context).prefs().get(data)
|
||||
is PackageParam -> (instance as PackageParam).prefs.get(data)
|
||||
else -> error("Unknown type for get prefs data")
|
||||
}
|
||||
|
||||
/**
|
||||
* 存入 [Boolean] 数据
|
||||
* @param data 键值数据模板
|
||||
* @param value 键值内容
|
||||
*/
|
||||
internal fun putBoolean(data: PrefsData<Boolean>, value: Boolean) {
|
||||
when (instance) {
|
||||
is Context -> (instance as Context).prefs().edit { put(data, value) }
|
||||
is PackageParam -> YLog.warn("Not support for this method")
|
||||
else -> error("Unknown type for put prefs data")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用模块
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableModule
|
||||
get() = getBoolean(ENABLE_MODULE)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_MODULE, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用模块日志
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableModuleLog
|
||||
get() = getBoolean(ENABLE_MODULE_LOG)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_MODULE_LOG, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用通知图标兼容模式
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableColorIconCompat
|
||||
get() = getBoolean(ENABLE_COLOR_ICON_COMPAT)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_COLOR_ICON_COMPAT, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否移除开发者选项警告通知
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableRemoveDevNotify
|
||||
get() = getBoolean(ENABLE_REMOVE_DEV_NOTIFY)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_REMOVE_DEV_NOTIFY, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否移除充电完成通知
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableRemoveChangeCompleteNotify
|
||||
get() = getBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否移除免打扰通知
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableRemoveDndAlertNotify
|
||||
get() = getBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用 material 3 通知图标风格
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableMd3NotifyIconStyle
|
||||
get() = getBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知栏中的通知图标圆角程度
|
||||
* @return [Int]
|
||||
*/
|
||||
var notifyIconCornerSize
|
||||
get() = getInt(NOTIFY_ICON_CORNER_SIZE)
|
||||
set(value) {
|
||||
putInt(NOTIFY_ICON_CORNER_SIZE, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否强制通知栏中的通知图标使用系统着色
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyIconForceSystemColor
|
||||
get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否强制通知栏中的通知图标为 APP 图标
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyIconForceAppIcon
|
||||
get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用媒体通知播放时自动展开
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyMediaPanelAutoExp
|
||||
get() = getBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用自定义通知面板背景透明度
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyPanelAlpha
|
||||
get() = getBoolean(ENABLE_NOTIFY_PANEL_ALPHA)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_PANEL_ALPHA, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义通知面板背景透明度
|
||||
* @return [Int]
|
||||
*/
|
||||
var notifyPanelAlphaLevel
|
||||
get() = getInt(NOTIFY_PANEL_ALPHA_LEVEL)
|
||||
set(value) {
|
||||
putInt(NOTIFY_PANEL_ALPHA_LEVEL, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用通知图标优化
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyIconFix
|
||||
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_ICON_FIX, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否使用占位符修补未适配的通知图标
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyIconFixPlaceholder
|
||||
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否提醒未适配通知图标的新安装应用
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyIconFixNotify
|
||||
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用通知图标优化名单自动更新
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableNotifyIconFixAuto
|
||||
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知图标优化名单自动更新时间
|
||||
* @return [String]
|
||||
*/
|
||||
var notifyIconFixAutoTime
|
||||
get() = getString(NOTIFY_ICON_FIX_AUTO_TIME)
|
||||
set(value) {
|
||||
putString(NOTIFY_ICON_FIX_AUTO_TIME, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知图标优化名单同步方式
|
||||
* @return [Int]
|
||||
*/
|
||||
var iconRuleSourceSyncType
|
||||
get() = getInt(ICON_RULE_SOURCE_SYNC_TYPE)
|
||||
set(value) {
|
||||
putInt(ICON_RULE_SOURCE_SYNC_TYPE, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知图标优化名单同步地址
|
||||
* @return [String]
|
||||
*/
|
||||
var iconRuleSourceSyncCustomUrl
|
||||
get() = getString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL)
|
||||
set(value) {
|
||||
putString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL, value)
|
||||
}
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/28.
|
||||
*/
|
||||
package com.fankes.coloros.notify.data
|
||||
|
||||
import com.fankes.coloros.notify.hook.HookConst
|
||||
import com.fankes.coloros.notify.utils.factory.isUpperOfAndroidS
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
|
||||
object DataConst {
|
||||
|
||||
val ENABLE_MODULE = PrefsData("_enable_module", true)
|
||||
val ENABLE_MODULE_LOG = PrefsData("_enable_module_log", false)
|
||||
val ENABLE_HIDE_ICON = PrefsData("_hide_icon", false)
|
||||
val ENABLE_ANDROID12_STYLE = PrefsData("_notify_android12_style", isUpperOfAndroidS)
|
||||
val ENABLE_NOTIFY_ICON_FIX = PrefsData("_notify_icon_fix", true)
|
||||
val ENABLE_NOTIFY_ICON_FIX_NOTIFY = PrefsData("_notify_icon_fix_notify", true)
|
||||
val REMOVE_DEV_NOTIFY = PrefsData("_remove_dev_notify", true)
|
||||
val REMOVE_CHANGECP_NOTIFY = PrefsData("_remove_charge_complete_notify", false)
|
||||
val REMOVE_DNDALERT_NOTIFY = PrefsData("_remove_dndalert_notify", false)
|
||||
val ENABLE_NOTIFY_ICON_FIX_AUTO = PrefsData("_enable_notify_icon_fix_auto", true)
|
||||
val NOTIFY_ICON_DATAS = PrefsData("_notify_icon_datas", "")
|
||||
val NOTIFY_ICON_FIX_AUTO_TIME = PrefsData("_notify_icon_fix_auto_time", "07:00")
|
||||
|
||||
val SOURCE_SYNC_WAY = PrefsData("_rule_source_sync_way", HookConst.TYPE_SOURCE_SYNC_WAY_1)
|
||||
val SOURCE_SYNC_WAY_CUSTOM_URL = PrefsData("_rule_source_sync_way_custom_url", "")
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/2/3.
|
||||
*/
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.coloros.notify.data.factory
|
||||
|
||||
import android.widget.CompoundButton
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
|
||||
/**
|
||||
* 绑定到 [CompoundButton] 自动设置选中状态
|
||||
* @param data 键值数据模板
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
fun CompoundButton.bind(data: PrefsData<Boolean>, initiate: CompoundButtonDataBinder.(CompoundButton) -> Unit = {}) {
|
||||
val binder = CompoundButtonDataBinder(button = this).also { initiate(it, this) }
|
||||
isChecked = ConfigData.getBoolean(data).also { binder.initializeCallback?.invoke(it) }
|
||||
binder.applyChangesCallback = { ConfigData.putBoolean(data, it) }
|
||||
setOnCheckedChangeListener { button, isChecked ->
|
||||
if (button.isPressed) {
|
||||
if (binder.isAutoApplyChanges) binder.applyChangesCallback?.invoke(isChecked)
|
||||
binder.changedCallback?.invoke(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [CompoundButton] 数据绑定管理器实例
|
||||
* @param button 当前实例
|
||||
*/
|
||||
class CompoundButtonDataBinder(private val button: CompoundButton) {
|
||||
|
||||
/** 状态初始化回调事件 */
|
||||
internal var initializeCallback: ((Boolean) -> Unit)? = null
|
||||
|
||||
/** 状态改变回调事件 */
|
||||
internal var changedCallback: ((Boolean) -> Unit)? = null
|
||||
|
||||
/** 应用更改回调事件 */
|
||||
internal var applyChangesCallback: ((Boolean) -> Unit)? = null
|
||||
|
||||
/** 是否启用自动应用更改 */
|
||||
var isAutoApplyChanges = true
|
||||
|
||||
/**
|
||||
* 监听状态初始化
|
||||
* @param result 回调结果
|
||||
*/
|
||||
fun onInitialize(result: (Boolean) -> Unit) {
|
||||
initializeCallback = result
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听状态改变
|
||||
* @param result 回调结果
|
||||
*/
|
||||
fun onChanged(result: (Boolean) -> Unit) {
|
||||
changedCallback = result
|
||||
}
|
||||
|
||||
/** 重新初始化 */
|
||||
fun reinitialize() {
|
||||
initializeCallback?.invoke(button.isChecked)
|
||||
}
|
||||
|
||||
/** 应用更改并重新初始化 */
|
||||
fun applyChangesAndReinitialize() {
|
||||
applyChanges()
|
||||
reinitialize()
|
||||
}
|
||||
|
||||
/** 应用更改 */
|
||||
fun applyChanges() {
|
||||
applyChangesCallback?.invoke(button.isChecked)
|
||||
}
|
||||
|
||||
/** 取消更改 */
|
||||
fun cancelChanges() {
|
||||
button.isChecked = button.isChecked.not()
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/2/4.
|
||||
*/
|
||||
@file:Suppress("SetTextI18n")
|
||||
|
||||
package com.fankes.coloros.notify.data.factory
|
||||
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
|
||||
/**
|
||||
* 绑定到 [SeekBar] 自动设置进度与 [TextView]
|
||||
* @param data 键值数据模板
|
||||
* @param textView 文本框
|
||||
* @param suffix 文本显示的后缀 - 默认空
|
||||
* @param onChange 当改变停止时回调
|
||||
*/
|
||||
fun SeekBar.bind(data: PrefsData<Int>, textView: TextView, suffix: String = "", onChange: (Int) -> Unit = {}) {
|
||||
ConfigData.getInt(data).also { value ->
|
||||
textView.text = "$value$suffix"
|
||||
progress = value
|
||||
}
|
||||
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
textView.text = "$progress$suffix"
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
ConfigData.putInt(data, seekBar.progress)
|
||||
onChange(seekBar.progress)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,36 +18,41 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/2/26.
|
||||
* This file is created by fankes on 2022/2/26.
|
||||
*/
|
||||
package com.fankes.coloros.notify.hook
|
||||
|
||||
import com.fankes.coloros.notify.data.DataConst
|
||||
import com.fankes.coloros.notify.hook.HookConst.SYSTEMUI_PACKAGE_NAME
|
||||
import com.fankes.coloros.notify.const.PackageName
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.fankes.coloros.notify.hook.entity.FrameworkHooker
|
||||
import com.fankes.coloros.notify.hook.entity.SystemUIHooker
|
||||
import com.fankes.coloros.notify.utils.factory.isNotColorOS
|
||||
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
|
||||
import com.highcapable.yukihookapi.hook.factory.configs
|
||||
import com.highcapable.yukihookapi.hook.factory.encase
|
||||
import com.highcapable.yukihookapi.hook.log.loggerW
|
||||
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||
|
||||
@InjectYukiHookWithXposed
|
||||
class HookEntry : YukiHookXposedInitProxy {
|
||||
object HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onInit() = configs {
|
||||
debugTag = "ColorOSNotify"
|
||||
debugLog {
|
||||
tag = "ColorOSNotifyIcon"
|
||||
isRecord = true
|
||||
elements(PRIORITY)
|
||||
}
|
||||
isDebug = false
|
||||
}
|
||||
|
||||
override fun onHook() = encase {
|
||||
when {
|
||||
/** 不是 ColorOS 系统停止 Hook */
|
||||
isNotColorOS -> loggerW(msg = "Aborted Hook -> This System is not ColorOS")
|
||||
/** Hook 被手动关闭停止 Hook */
|
||||
prefs.get(DataConst.ENABLE_MODULE).not() -> loggerW(msg = "Aborted Hook -> Hook Closed")
|
||||
/** 开始 Hook */
|
||||
else -> loadApp(SYSTEMUI_PACKAGE_NAME, SystemUIHooker())
|
||||
if (isNotColorOS) return@encase YLog.warn("Aborted Hook -> This System is not ColorOS")
|
||||
loadSystem(FrameworkHooker)
|
||||
loadApp(PackageName.SYSTEMUI) {
|
||||
ConfigData.init(instance = this)
|
||||
if (ConfigData.isEnableModule)
|
||||
loadHooker(SystemUIHooker)
|
||||
else YLog.warn("Aborted Hook -> Hook Closed")
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by Nep-Timeline on 2025/5/27.
|
||||
*/
|
||||
package com.fankes.coloros.notify.hook.entity
|
||||
|
||||
import android.app.Notification
|
||||
import com.highcapable.kavaref.KavaRef.Companion.resolve
|
||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
|
||||
/**
|
||||
* 系统框架核心 Hook 类
|
||||
*/
|
||||
object FrameworkHooker : YukiBaseHooker() {
|
||||
|
||||
/** ColorOS 存在的类 - 旧版本不存在 */
|
||||
private val OplusNotificationFixHelperClass by lazyClassOrNull("com.android.server.notification.OplusNotificationFixHelper")
|
||||
|
||||
override fun onHook() {
|
||||
/** 拦截 ColorOS 覆盖应用通知图标 */
|
||||
OplusNotificationFixHelperClass?.resolve()?.optional()?.firstMethodOrNull {
|
||||
name = "fixSmallIcon"
|
||||
parameters(Notification::class, String::class, String::class, Boolean::class)
|
||||
}?.hook()?.intercept()
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,19 +18,22 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/24.
|
||||
* This file is created by fankes on 2022/1/24.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.coloros.notify.param
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import com.fankes.coloros.notify.bean.IconDataBean
|
||||
import com.fankes.coloros.notify.data.DataConst
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.fankes.coloros.notify.utils.factory.bitmap
|
||||
import com.fankes.coloros.notify.utils.factory.safeOf
|
||||
import com.fankes.coloros.notify.utils.factory.safeOfNan
|
||||
import com.fankes.coloros.notify.utils.factory.safeOfNull
|
||||
import com.fankes.coloros.notify.utils.factory.snake
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
@@ -44,64 +47,11 @@ import org.json.JSONObject
|
||||
*/
|
||||
class IconPackParams(private val context: Context? = null, private val param: PackageParam? = null) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Android 11 系统默认图标
|
||||
* @return [Bitmap]
|
||||
*/
|
||||
val android11IconBitmap by lazy {
|
||||
("iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
|
||||
"CHwIZIgAAAPkSURBVHic7ZvfVdswFMY/9fS93qDZoNmg6QS4E8AGdAMYgQ1gg4QJkk5AOkGcCUgm\n" +
|
||||
"+Pog27EdCa7+WDIn/J4gx77W/SzpyvdKwCefdFE5H06yAPCj/vefUuqQqy1JhCA5B3AFYFH/NAdQ\n" +
|
||||
"WC4/ANjWf28APCultpZrozGaECQbx0sAs0BzFYAVgI1S6jm0baNDsiB5R/KV4/FK8i63r1ZIXpPc\n" +
|
||||
"jSjAkB3J69x+t5AsEwtgEqTMLcJjRgGGPOYQoCC5zu25gTV1SE4iwpx5h8J77KjDtRNO4ZPkAsAS\n" +
|
||||
"9jXAVDgA+K2U2khvEAtRq7zG9EVoOAD4JV2MfZFcRD3uXHvCEcBfh+vf429tU0oBYMmYcwbJF+H4\n" +
|
||||
"3FKH09ng/jnJG5IHh7F+qO+ZD2zN6mdshXbWsUSQhsh7ga2C5Epga0XBmyR5L2xbWGilVl6C04KG\n" +
|
||||
"5MMbth6m0MbhQyRhcuVhtyBZGWxV9BjTlPWynavdxviNwLhXw2v7C4M95/hf27IJO+TGx7CkNzh1\n" +
|
||||
"Y8Nzuo0PyjmQfBK019orbOHzD2Q5hMqr1Se6zse0ZWNGy6RuE+I24sOl98e09RZG386EoJ5dU60e\n" +
|
||||
"xUvgiBQ0RBBTj1gYfkuB10Tpef+ZjyYhrvzbEsT3wPtdhDjzsSdEHb5CE62+zEPCJ4CfDrfMhs8a\n" +
|
||||
"9oi8KS//j6RHuL/A3vAYCpFrfmiYAXj3m6VLPfH5vMDePaLP8MTckhT1DOq0/jJ6C+hejwjqQTQv\n" +
|
||||
"sxteqUsEps/wK8pTA1b7XbtfB22bUvapAPAEACQBveZ4q1ToY7+lHRpMlf31Z4HIL6rrc3eOCF3Q\n" +
|
||||
"fERan6c4WWahK8TopfcJ0vrcCpFzk0Yuuj5f8tDolQaGQsSsQ0yd3lRwyT2ix1CIHImSXPSy70Mh\n" +
|
||||
"nFPzH5jeS+8JURdM90mbk4f9sDhsmiMuoVec+WgS4hLmiTMfz4RQSq3gVn4PIUci6Fj72MMWPqUV\n" +
|
||||
"rJSZ51jIq3OU1xJDS36p92K5V9MoKwLvGLcIPDZuReBOYyW9wjlnSHmROSb+tVXm2SgyFmGlCsrK\n" +
|
||||
"7aRgozh1T1iO56uVpyAROg5shA98oc4wmzaTXXPcXfs2ROsi0T5L6glxC7f6ZHMAJWfRaA9gLkk6\n" +
|
||||
"uW443QD4FtCwlBwBLKJuOAXaD7IS6VadIRwBlKMegarHuySs5qKiZ1XdR4yC8gk0JRvmKFRRHlpT\n" +
|
||||
"ECdEBohRMu9QqZj7KFMX6m+TlIJU9P12SAH1RnGXXfiuHCjYAO/KmAdgS5wOwIZuFNvjdAB2lFRi\n" +
|
||||
"yiPRjTCATsjYFmZHnIovjfMf90i0BOpQ18T87SXWXz+ZKv8BVnFXPlKejoIAAAAASUVORK5CYII=").bitmap
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 12 系统默认图标
|
||||
* @return [Bitmap]
|
||||
*/
|
||||
val android12IconBitmap by lazy {
|
||||
("iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
|
||||
"CHwIZIgAAANkSURBVHic7ZvNkRoxEIWfXL4vIbBVvpsMTAhksIRgRwAZsBnAxWfjCMARGM4+MBkw\n" +
|
||||
"RPB8kKh1zagljUYzwlv6qrZ2a0fo50lq9agboFAoFApeVI5GSX4xf07NDwDUAE4AbkqpU45+9Ybk\n" +
|
||||
"hOTK8/yF5JbklX6uJH+QfPG0uyI5ST+iCMwgf5sBbC3PVoGDd4nSGrARlabtvGI0RLizNc9eegrQ\n" +
|
||||
"5EJy0RDhTj4xBBHu/EkoQGjdvcT40EOLKYBn4dmnHvX6kOp+xpvh7UyvU4PkDMARwFOHj50B7KBP\n" +
|
||||
"iEopVZmZnOHtFFkA+NyhzhuAedbThuSMZB2wpHckg2eM5JzkMaDem5mQ/JD86ehoTXLeo+6lR+jv\n" +
|
||||
"KccSjZk5iRMTWHP6V1200MmgPtYGE+GfdlxiXFK1E9u5pdCxmh3sQYf2Jg4xlqnb69IxaTUsBmxz\n" +
|
||||
"LbR5GKpNX4dmQoeOI7RdCW1Hnx59HCrJQL02/0FySnJD8kDtHovbJrDsumOfhoN297oWyjZnUDRu\n" +
|
||||
"oWVptxX7VOMLRliaO0s56XhtLeOOZfe2grHj6bM1bFSJ63OR//KGsqG0nhZsL3dRsNCyJBehqyeE\n" +
|
||||
"jzEfAiA5SlYbAW3EvkK/WFWQjV2XshWAX96eDoljL+d3dSNJbSP+W4oQhiKEoQhhiD01JDYUvMsR\n" +
|
||||
"+RZzZZdaiEe4Mou6Aylbw1CEMBQhDLE2okZu91bbgi6xj/dJaje/bA1DEcJQhDAUIQxRp4a5BdpY\n" +
|
||||
"HkW5t49AHxfbZp2nGO8uUQoJRN2bRm0Nx6yP+a5hFUIpNZ4QDrILMTq0xxWuI7ZvS1SL3pZ9VoQt\n" +
|
||||
"xjnhCFFp6rCB7XW7FWAaHMqxjcFzFajjosliGik6dBI65Ipb9G1TCuyMGWVrdUpKFLlygARQ6ki5\n" +
|
||||
"lMSaL1HEdE7KVbikXKp0J7jmd+LoTia7phDDIwL5KBE2kq8eMZxZ9p6653TndI9/UkiYGZMM551D\n" +
|
||||
"l5mjtgfN5PMmydKUkn1xhdo4HuG/PjsB2EO/E1QAzkqpmnoLPUF7p0v4vdQzdNpx7jhKm8CVkYKk\n" +
|
||||
"OZyDYMTYDSjC49iEEKgdH+lojaFmbl8hFurVsWZY9r5LgDUffSuEQu2F7gNFqam313IsAXJ93XEG\n" +
|
||||
"/fZ4/w3o06QGAKXU4Nm7hUKhUIjgL/9/6dhvvYPfAAAAAElFTkSuQmCC").bitmap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 已存储的 JSON 数据
|
||||
* @return [String]
|
||||
*/
|
||||
internal val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.direct()?.get(DataConst.NOTIFY_ICON_DATAS)
|
||||
internal val storageDataJson get() = (context?.prefs() ?: param?.prefs)?.get(ConfigData.NOTIFY_ICONS_DATA)
|
||||
|
||||
/**
|
||||
* 获取图标数据
|
||||
@@ -146,7 +96,7 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
|
||||
* @return [String] 拼接后的数据
|
||||
*/
|
||||
fun splicingJsonArray(dataJson1: String, dataJson2: String) = safeOf(default = "[]") {
|
||||
dataJson1.replace(oldValue = "]", newValue = "") + "," + dataJson2.replace(oldValue = "[", newValue = "")
|
||||
dataJson1.replace("]", "") + "," + dataJson2.replace("[", "")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,7 +125,7 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
|
||||
* @param json 数据
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun isHackString(json: String) = json.contains(other = "Checking your browser before accessing")
|
||||
fun isHackString(json: String) = json.contains("Checking your browser before accessing")
|
||||
|
||||
/**
|
||||
* 比较图标数据不相等
|
||||
@@ -188,5 +138,5 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
|
||||
* 保存图标数据
|
||||
* @param dataJson 图标数据 JSON
|
||||
*/
|
||||
fun save(dataJson: String) = context?.modulePrefs?.put(DataConst.NOTIFY_ICON_DATAS, dataJson)
|
||||
}
|
||||
fun save(dataJson: String) = context?.prefs()?.edit { put(ConfigData.NOTIFY_ICONS_DATA, dataJson) }
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,13 +18,14 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/2/15.
|
||||
* This file is created by fankes on 2022/2/15.
|
||||
* This file is Modified by fankes on 2023/2/3.
|
||||
*/
|
||||
package com.fankes.coloros.notify.hook.factory
|
||||
package com.fankes.coloros.notify.param.factory
|
||||
|
||||
import android.content.Context
|
||||
import com.fankes.coloros.notify.bean.IconDataBean
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
|
||||
/**
|
||||
@@ -37,14 +38,14 @@ fun PackageParam.isAppNotifyHookOf(bean: IconDataBean) = prefs.getBoolean(bean.t
|
||||
* 获取此 APP 的通知图标是否被 Hook
|
||||
* @param bean 图标 bean
|
||||
*/
|
||||
fun Context.isAppNotifyHookOf(bean: IconDataBean) = modulePrefs.getBoolean(bean.toEnabledName(), bean.isEnabled)
|
||||
fun Context.isAppNotifyHookOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledName(), bean.isEnabled)
|
||||
|
||||
/**
|
||||
* 设置 Hook 此 APP 的通知图标
|
||||
* @param bean 图标 bean
|
||||
* @param isHook 是否 Hook
|
||||
*/
|
||||
fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = modulePrefs.putBoolean(bean.toEnabledName(), isHook)
|
||||
fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledName(), isHook) }
|
||||
|
||||
/**
|
||||
* 获取此 APP 的通知图标是否被全部 Hook
|
||||
@@ -56,11 +57,11 @@ fun PackageParam.isAppNotifyHookAllOf(bean: IconDataBean) = prefs.getBoolean(bea
|
||||
* 获取此 APP 的通知图标是否被全部 Hook
|
||||
* @param bean 图标 bean
|
||||
*/
|
||||
fun Context.isAppNotifyHookAllOf(bean: IconDataBean) = modulePrefs.getBoolean(bean.toEnabledAllName(), bean.isEnabledAll)
|
||||
fun Context.isAppNotifyHookAllOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledAllName(), bean.isEnabledAll)
|
||||
|
||||
/**
|
||||
* 设置全部 Hook 此 APP 的通知图标
|
||||
* @param bean 图标 bean
|
||||
* @param isHook 是否 Hook
|
||||
*/
|
||||
fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = modulePrefs.putBoolean(bean.toEnabledAllName(), isHook)
|
||||
fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledAllName(), isHook) }
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,7 +18,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/26.
|
||||
* This file is created by fankes on 2022/3/26.
|
||||
*/
|
||||
package com.fankes.coloros.notify.service
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,32 +18,38 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/30.
|
||||
* This file is created by fankes on 2022/1/30.
|
||||
*/
|
||||
@file:Suppress("SetTextI18n", "InflateParams", "DEPRECATION")
|
||||
@file:Suppress("SetTextI18n", "InflateParams")
|
||||
|
||||
package com.fankes.coloros.notify.ui.activity
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.coloros.notify.R
|
||||
import com.fankes.coloros.notify.bean.IconDataBean
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.fankes.coloros.notify.databinding.ActivityConfigBinding
|
||||
import com.fankes.coloros.notify.databinding.AdapterConfigBinding
|
||||
import com.fankes.coloros.notify.databinding.DiaIconFilterBinding
|
||||
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf
|
||||
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf
|
||||
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookAllOf
|
||||
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookOf
|
||||
import com.fankes.coloros.notify.param.IconPackParams
|
||||
import com.fankes.coloros.notify.param.factory.isAppNotifyHookAllOf
|
||||
import com.fankes.coloros.notify.param.factory.isAppNotifyHookOf
|
||||
import com.fankes.coloros.notify.param.factory.putAppNotifyHookAllOf
|
||||
import com.fankes.coloros.notify.param.factory.putAppNotifyHookOf
|
||||
import com.fankes.coloros.notify.ui.activity.base.BaseActivity
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import com.fankes.coloros.notify.utils.factory.addOnBackPressedEvent
|
||||
import com.fankes.coloros.notify.utils.factory.bindAdapter
|
||||
import com.fankes.coloros.notify.utils.factory.callOnBackPressed
|
||||
import com.fankes.coloros.notify.utils.factory.colorOf
|
||||
import com.fankes.coloros.notify.utils.factory.copyToClipboard
|
||||
import com.fankes.coloros.notify.utils.factory.navigate
|
||||
import com.fankes.coloros.notify.utils.factory.openBrowser
|
||||
import com.fankes.coloros.notify.utils.factory.showDialog
|
||||
import com.fankes.coloros.notify.utils.factory.snake
|
||||
import com.fankes.coloros.notify.utils.factory.toast
|
||||
import com.fankes.coloros.notify.utils.tool.IconRuleManagerTool
|
||||
import com.fankes.coloros.notify.utils.tool.SystemUITool
|
||||
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
|
||||
class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
|
||||
@@ -60,18 +66,18 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
private var iconAllDatas = ArrayList<IconDataBean>()
|
||||
|
||||
override fun onCreate() {
|
||||
/** 检查激活状态 */
|
||||
if (isXposedModuleActive.not()) {
|
||||
/** 检查激活和启用状态 */
|
||||
if (YukiHookAPI.Status.isXposedModuleActive.not() || ConfigData.isEnableModule.not()) {
|
||||
showDialog {
|
||||
title = "模块没有激活"
|
||||
msg = "模块没有激活,你无法使用这里的功能,请先激活模块。"
|
||||
title = "模块不可用"
|
||||
msg = "模块没有激活或已被停用,你无法使用这里的功能,请先激活或启用模块。"
|
||||
confirmButton(text = "我知道了") { finish() }
|
||||
noCancelable()
|
||||
}
|
||||
return
|
||||
}
|
||||
/** 返回按钮点击事件 */
|
||||
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
||||
binding.titleBackIcon.setOnClickListener { callOnBackPressed() }
|
||||
/** 刷新适配器结果相关 */
|
||||
refreshAdapterResult()
|
||||
/** 设置上下按钮点击事件 */
|
||||
@@ -85,9 +91,9 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
}
|
||||
/** 设置过滤按钮点击事件 */
|
||||
binding.configTitleFilter.setOnClickListener {
|
||||
showDialog {
|
||||
showDialog<DiaIconFilterBinding> {
|
||||
title = "按条件过滤"
|
||||
val editText = bind<DiaIconFilterBinding>().diaIconFilterInputEdit.apply {
|
||||
binding.iconFiltersEdit.apply {
|
||||
requestFocus()
|
||||
invalidate()
|
||||
if (filterText.isNotBlank()) {
|
||||
@@ -96,8 +102,8 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
}
|
||||
}
|
||||
confirmButton {
|
||||
if (editText.text.toString().isNotBlank()) {
|
||||
filterText = editText.text.toString().trim()
|
||||
if (binding.iconFiltersEdit.text.toString().isNotBlank()) {
|
||||
filterText = binding.iconFiltersEdit.text.toString().trim()
|
||||
refreshAdapterResult()
|
||||
} else {
|
||||
toast(msg = "条件不能为空")
|
||||
@@ -116,49 +122,46 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
binding.configTitleSync.setOnClickListener { onStartRefresh() }
|
||||
/** 设置列表元素和 Adapter */
|
||||
binding.configListView.apply {
|
||||
adapter = object : BaseAdapter() {
|
||||
|
||||
override fun getCount() = iconDatas.size
|
||||
|
||||
override fun getItem(position: Int) = iconDatas[position]
|
||||
|
||||
override fun getItemId(position: Int) = position.toLong()
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||
var cView = convertView
|
||||
val holder: AdapterConfigBinding
|
||||
if (convertView == null) {
|
||||
holder = AdapterConfigBinding.inflate(LayoutInflater.from(context))
|
||||
cView = holder.root
|
||||
cView.tag = holder
|
||||
} else holder = convertView.tag as AdapterConfigBinding
|
||||
getItem(position).also {
|
||||
holder.adpAppIcon.setImageBitmap(it.iconBitmap)
|
||||
(if (it.iconColor != 0) it.iconColor else resources.getColor(R.color.colorTextGray)).also { color ->
|
||||
holder.adpAppIcon.setColorFilter(color)
|
||||
holder.adpAppName.setTextColor(color)
|
||||
bindAdapter {
|
||||
onBindDatas { iconDatas }
|
||||
onBindViews<AdapterConfigBinding> { binding, position ->
|
||||
iconDatas[position].also { bean ->
|
||||
binding.adpAppIcon.setImageBitmap(bean.iconBitmap)
|
||||
(if (bean.iconColor != 0) bean.iconColor else resources.colorOf(R.color.colorTextGray)).also { color ->
|
||||
binding.adpAppIcon.setColorFilter(color)
|
||||
binding.adpAppName.setTextColor(color)
|
||||
}
|
||||
holder.adpAppName.text = it.appName
|
||||
holder.adpAppPkgName.text = it.packageName
|
||||
holder.adpCbrName.text = "贡献者:" + it.contributorName
|
||||
isAppNotifyHookOf(it).also { e ->
|
||||
holder.adpAppOpenSwitch.isChecked = e
|
||||
holder.adpAppAllSwitch.isEnabled = e
|
||||
binding.adpAppName.text = bean.appName
|
||||
binding.adpAppPkgName.text = bean.packageName
|
||||
binding.adpCbrName.text = "贡献者:" + bean.contributorName
|
||||
isAppNotifyHookOf(bean).also { e ->
|
||||
binding.adpAppOpenSwitch.isChecked = e
|
||||
binding.adpAppAllSwitch.isEnabled = e
|
||||
}
|
||||
holder.adpAppOpenSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
binding.adpAppOpenSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
putAppNotifyHookOf(it, b)
|
||||
holder.adpAppAllSwitch.isEnabled = b
|
||||
putAppNotifyHookOf(bean, b)
|
||||
binding.adpAppAllSwitch.isEnabled = b
|
||||
SystemUITool.refreshSystemUI(context = this@ConfigureActivity)
|
||||
}
|
||||
holder.adpAppAllSwitch.isChecked = isAppNotifyHookAllOf(it)
|
||||
holder.adpAppAllSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
binding.adpAppAllSwitch.isChecked = isAppNotifyHookAllOf(bean)
|
||||
binding.adpAppAllSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
putAppNotifyHookAllOf(it, b)
|
||||
SystemUITool.refreshSystemUI(context = this@ConfigureActivity)
|
||||
fun saveState() {
|
||||
putAppNotifyHookAllOf(bean, b)
|
||||
SystemUITool.refreshSystemUI(context = this@ConfigureActivity)
|
||||
}
|
||||
if (b) showDialog {
|
||||
title = "全部替换"
|
||||
msg = "此功能仅针对严重不遵守规范的 APP 通知图标才需要开启,例如:APP 推送通知后无法识别出现的黑白块图标。\n\n" +
|
||||
"此功能在一般情况下请保持关闭并跟随在线规则的配置,并不要随意改变此配置," +
|
||||
"开启后 APP 的通知图标可能会被规则破坏,你确定还要开启吗?"
|
||||
confirmButton { saveState() }
|
||||
cancelButton { btn.isChecked = btn.isChecked.not() }
|
||||
noCancelable()
|
||||
} else saveState()
|
||||
}
|
||||
}
|
||||
return cView!!
|
||||
}
|
||||
}.apply {
|
||||
setOnItemLongClickListener { _, _, p, _ ->
|
||||
@@ -176,7 +179,13 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
}
|
||||
/** 设置点击事件 */
|
||||
binding.configCbrButton.setOnClickListener {
|
||||
openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md")
|
||||
showDialog {
|
||||
title = "感谢你的贡献"
|
||||
msg = "通知图标优化名单需要大家的共同维护才能得以完善,请选择你的贡献方式。"
|
||||
confirmButton(text = "贡献规则") { openBrowser(IconRuleManagerTool.RULES_CONTRIBUTING_URL) }
|
||||
cancelButton(text = "请求适配") { openBrowser(IconRuleManagerTool.RULES_FEEDBACK_URL) }
|
||||
neutralButton(text = "暂时不用")
|
||||
}
|
||||
}
|
||||
/** 装载数据 */
|
||||
mockLocalData()
|
||||
@@ -188,30 +197,56 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
val pkgName = intent?.getStringExtra("pkgName") ?: ""
|
||||
title = "新安装应用通知图标适配"
|
||||
msg = "你已安装 $appName($pkgName)\n\n" +
|
||||
"此应用未在通知优化名单中发现适配数据,若此应用发送的通知为彩色图标," +
|
||||
"可随时点击本页面下方的“贡献通知图标优化名单”按钮提交贡献或请求适配。\n\n" +
|
||||
"若你已知晓此应用会遵守原生通知图标规范,可忽略此提示。\n\n" +
|
||||
"你可以现在立即同步适配列表,以获取最新的适配数据。"
|
||||
"此应用未在通知优化名单中发现适配数据,若此应用发送的通知为彩色图标," +
|
||||
"可随时点击本页面下方的“贡献通知图标优化名单”按钮提交贡献或请求适配。\n\n" +
|
||||
"若你已知晓此应用会遵守原生通知图标规范,可忽略此提示。\n\n" +
|
||||
"你可以现在立即同步适配列表,以获取最新的适配数据。"
|
||||
confirmButton(text = "同步列表") { onStartRefresh() }
|
||||
cancelButton(text = "复制名称+包名") { copyToClipboard(content = "$appName($pkgName)") }
|
||||
neutralButton(text = "取消")
|
||||
noCancelable()
|
||||
}
|
||||
intent?.getBooleanExtra("isDirectUpdate", false) == true -> onStartRefresh(isByHand = false)
|
||||
intent?.getBooleanExtra("isShowUpdDialog", true) == true -> onStartRefresh()
|
||||
}
|
||||
/** 清除数据 */
|
||||
intent?.apply {
|
||||
removeExtra("isNewAppSupport")
|
||||
removeExtra("isDirectUpdate")
|
||||
removeExtra("isShowUpdDialog")
|
||||
}
|
||||
/** 设置返回监听事件 */
|
||||
addOnBackPressedEvent {
|
||||
if (MainActivity.isActivityLive.not())
|
||||
showDialog {
|
||||
title = "提示"
|
||||
msg = "要返回模块主页吗?"
|
||||
confirmButton {
|
||||
releaseEventAndBack()
|
||||
navigate<MainActivity>()
|
||||
}
|
||||
cancelButton { releaseEventAndBack() }
|
||||
}
|
||||
else releaseEventAndBack()
|
||||
}
|
||||
}
|
||||
|
||||
/** 开始手动同步 */
|
||||
private fun onStartRefresh() =
|
||||
IconRuleManagerTool.syncByHand(context = this) {
|
||||
filterText = ""
|
||||
mockLocalData()
|
||||
}
|
||||
/**
|
||||
* 开始同步
|
||||
* @param isByHand 是否手动同步 - 默认是
|
||||
*/
|
||||
private fun onStartRefresh(isByHand: Boolean = true) {
|
||||
if (isByHand)
|
||||
IconRuleManagerTool.syncByHand(context = this) {
|
||||
filterText = ""
|
||||
mockLocalData()
|
||||
}
|
||||
else
|
||||
IconRuleManagerTool.sync(context = this) {
|
||||
filterText = ""
|
||||
mockLocalData()
|
||||
}
|
||||
}
|
||||
|
||||
/** 装载或刷新本地数据 */
|
||||
private fun mockLocalData() {
|
||||
@@ -224,7 +259,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
onChanged?.invoke()
|
||||
binding.configTitleCountText.text =
|
||||
if (filterText.isBlank()) "已适配 ${iconDatas.size} 个 APP 的通知图标"
|
||||
else "“${filterText}” 匹配到 ${iconDatas.size} 个结果"
|
||||
else "“$filterText” 匹配到 ${iconDatas.size} 个结果"
|
||||
binding.configListNoDataView.apply {
|
||||
text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~"
|
||||
isVisible = iconDatas.isEmpty()
|
||||
@@ -240,18 +275,4 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
else iconAllDatas.filter {
|
||||
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (MainActivity.isActivityLive.not())
|
||||
showDialog {
|
||||
title = "提示"
|
||||
msg = "要返回模块主页吗?"
|
||||
confirmButton {
|
||||
super.onBackPressed()
|
||||
navigate<MainActivity>()
|
||||
}
|
||||
cancelButton { super.onBackPressed() }
|
||||
}
|
||||
else super.onBackPressed()
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,27 +18,37 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/2/26.
|
||||
* This file is created by fankes on 2022/2/26.
|
||||
*/
|
||||
@file:Suppress("SetTextI18n")
|
||||
|
||||
package com.fankes.coloros.notify.ui.activity
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.coloros.notify.BuildConfig
|
||||
import com.fankes.coloros.notify.R
|
||||
import com.fankes.coloros.notify.data.DataConst
|
||||
import com.fankes.coloros.notify.const.ModuleVersion
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.fankes.coloros.notify.data.factory.bind
|
||||
import com.fankes.coloros.notify.databinding.ActivityMainBinding
|
||||
import com.fankes.coloros.notify.param.IconPackParams
|
||||
import com.fankes.coloros.notify.ui.activity.base.BaseActivity
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import com.fankes.coloros.notify.utils.factory.androidVersionCodeName
|
||||
import com.fankes.coloros.notify.utils.factory.colorOSFullVersion
|
||||
import com.fankes.coloros.notify.utils.factory.colorOSNumberVersion
|
||||
import com.fankes.coloros.notify.utils.factory.hideOrShowLauncherIcon
|
||||
import com.fankes.coloros.notify.utils.factory.isLauncherIconShowing
|
||||
import com.fankes.coloros.notify.utils.factory.isNotColorOS
|
||||
import com.fankes.coloros.notify.utils.factory.isNotNoificationEnabled
|
||||
import com.fankes.coloros.notify.utils.factory.navigate
|
||||
import com.fankes.coloros.notify.utils.factory.openBrowser
|
||||
import com.fankes.coloros.notify.utils.factory.openNotifySetting
|
||||
import com.fankes.coloros.notify.utils.factory.showDialog
|
||||
import com.fankes.coloros.notify.utils.factory.showTimePicker
|
||||
import com.fankes.coloros.notify.utils.tool.GithubReleaseTool
|
||||
import com.fankes.coloros.notify.utils.tool.I18nWarnTool
|
||||
import com.fankes.coloros.notify.utils.tool.SystemUITool
|
||||
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
|
||||
import com.fankes.projectpromote.ProjectPromote
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
|
||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
@@ -47,27 +57,18 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
/** 窗口是否启动 */
|
||||
internal var isActivityLive = false
|
||||
|
||||
/** 模块版本 */
|
||||
private const val moduleVersion = BuildConfig.VERSION_NAME
|
||||
/** 模块是否可用 */
|
||||
internal var isModuleRegular = false
|
||||
|
||||
/** 预发布的版本标识 */
|
||||
private const val pendingFlag = ""
|
||||
/** 模块是否有效 */
|
||||
internal var isModuleValied = false
|
||||
}
|
||||
|
||||
/** 模块是否可用 */
|
||||
private var isModuleRegular = false
|
||||
|
||||
/** 模块是否有效 */
|
||||
private var isModuleValied = false
|
||||
|
||||
override fun onCreate() {
|
||||
/** 设置可用性 */
|
||||
isActivityLive = true
|
||||
/** 设置文本 */
|
||||
binding.mainTextVersion.text = "模块版本:$moduleVersion $pendingFlag"
|
||||
binding.mainTextColorOsVersion.text = "系统版本:$colorOSFullVersion"
|
||||
/** 检查更新 */
|
||||
GithubReleaseTool.checkingForUpdate(context = this, moduleVersion) { version, function ->
|
||||
GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
|
||||
binding.mainTextReleaseVersion.apply {
|
||||
text = "点击更新 $version"
|
||||
isVisible = true
|
||||
@@ -79,23 +80,26 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
isNotColorOS ->
|
||||
showDialog {
|
||||
title = "不是 ColorOS 系统"
|
||||
msg = "此模块专为 ColorOS 系统打造,当前无法识别你的系统为 ColorOS,所以模块无法工作。\n" +
|
||||
"如有问题请联系 酷安 @星夜不荟"
|
||||
confirmButton(text = "退出") { finish() }
|
||||
msg = "此模块专为 ColorOS 系统打造,当前无法识别你的系统为 ColorOS,所以模块无法工作。"
|
||||
confirmButton(text = "查看支持的模块") {
|
||||
openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt")
|
||||
finish()
|
||||
}
|
||||
cancelButton(text = "退出") { finish() }
|
||||
noCancelable()
|
||||
}
|
||||
/** 判断是否 Hook */
|
||||
isXposedModuleActive -> {
|
||||
if (IconPackParams(context = this).iconDatas.isEmpty() && modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX))
|
||||
YukiHookAPI.Status.isXposedModuleActive -> {
|
||||
if (IconPackParams(context = this).iconDatas.isEmpty() && ConfigData.isEnableNotifyIconFix)
|
||||
showDialog {
|
||||
title = "配置通知图标优化名单"
|
||||
msg = "模块需要获取在线规则以更新“通知图标优化名单”,它现在是空的,这看起来是你第一次使用模块,请首先进行配置才可以使用相关功能。\n" +
|
||||
"你可以随时在本页面下方找到“配置通知图标优化名单”手动前往。"
|
||||
"你可以随时在本页面下方找到“配置通知图标优化名单”手动前往。"
|
||||
confirmButton(text = "前往") { navigate<ConfigureActivity>() }
|
||||
cancelButton()
|
||||
noCancelable()
|
||||
}
|
||||
if (isNotNoificationEnabled && modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX))
|
||||
if (isNotNoificationEnabled && ConfigData.isEnableNotifyIconFix)
|
||||
showDialog {
|
||||
title = "模块的通知权限已关闭"
|
||||
msg = "请开启通知权限,以确保你能收到通知优化图标在线规则的更新。"
|
||||
@@ -103,116 +107,226 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
cancelButton()
|
||||
noCancelable()
|
||||
}
|
||||
/** 推广、恰饭 */
|
||||
ProjectPromote.show(activity = this, ModuleVersion.toString())
|
||||
}
|
||||
else ->
|
||||
showDialog {
|
||||
title = "模块没有激活"
|
||||
msg = "检测到模块没有激活,模块需要 Xposed 环境依赖," +
|
||||
"同时需要系统拥有 Root 权限," +
|
||||
"请自行查看本页面使用帮助与说明第二条。\n" +
|
||||
"由于需要修改系统应用达到效果,模块不支持太极阴、应用转生。"
|
||||
"同时需要系统拥有 Root 权限," +
|
||||
"请自行查看本页面使用帮助与说明第二条。\n" +
|
||||
"由于需要修改系统应用达到效果,模块不支持太极阴、应用转生。"
|
||||
confirmButton(text = "我知道了")
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
var notifyIconAutoSyncTime = modulePrefs.get(DataConst.NOTIFY_ICON_FIX_AUTO_TIME)
|
||||
binding.devNotifyConfigItem.isVisible = modulePrefs.get(DataConst.ENABLE_MODULE)
|
||||
binding.a12StyleConfigItem.isVisible = modulePrefs.get(DataConst.ENABLE_MODULE)
|
||||
binding.notifyIconConfigItem.isVisible = modulePrefs.get(DataConst.ENABLE_MODULE)
|
||||
binding.notifyIconFixButton.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
|
||||
binding.notifyIconFixNotifyItem.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
|
||||
binding.notifyIconAutoSyncItem.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
|
||||
binding.notifyIconAutoSyncChildItem.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_AUTO)
|
||||
binding.devNotifyConfigSwitch.isChecked = modulePrefs.get(DataConst.REMOVE_DEV_NOTIFY)
|
||||
binding.crcpNotifyConfigSwitch.isChecked = modulePrefs.get(DataConst.REMOVE_CHANGECP_NOTIFY)
|
||||
binding.dndNotifyConfigSwitch.isChecked = modulePrefs.get(DataConst.REMOVE_DNDALERT_NOTIFY)
|
||||
binding.a12StyleConfigSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_ANDROID12_STYLE)
|
||||
binding.moduleEnableSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_MODULE)
|
||||
binding.moduleEnableLogSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_MODULE_LOG)
|
||||
binding.hideIconInLauncherSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_HIDE_ICON)
|
||||
binding.notifyIconFixSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
|
||||
binding.notifyIconFixNotifySwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY)
|
||||
binding.notifyIconAutoSyncSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_AUTO)
|
||||
binding.notifyIconAutoSyncText.text = notifyIconAutoSyncTime
|
||||
binding.moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_MODULE, b)
|
||||
binding.moduleEnableLogSwitch.isVisible = b
|
||||
binding.notifyIconConfigItem.isVisible = b
|
||||
binding.devNotifyConfigItem.isVisible = b
|
||||
binding.a12StyleConfigItem.isVisible = b
|
||||
SystemUITool.showNeedRestartSnake(context = this)
|
||||
I18nWarnTool.checkingOrShowing(context = this)
|
||||
binding.mainTextVersion.text = "模块版本:${ModuleVersion.NAME}"
|
||||
/** 设置 CI 自动构建标识 */
|
||||
if (ModuleVersion.isCiMode)
|
||||
binding.mainTextReleaseVersion.apply {
|
||||
text = "CI ${ModuleVersion.GITHUB_COMMIT_ID}"
|
||||
isVisible = true
|
||||
setOnClickListener {
|
||||
showDialog {
|
||||
title = "CI 自动构建说明"
|
||||
msg = """
|
||||
你正在使用的是 CI 自动构建版本,Commit ID 为 ${ModuleVersion.GITHUB_COMMIT_ID}。
|
||||
|
||||
它是由代码提交后自动触发并构建、自动编译发布的,并未经任何稳定性测试,使用风险自负。
|
||||
""".trimIndent()
|
||||
confirmButton(text = "我知道了")
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.mainTextColorOsVersion.text = "系统版本:[$androidVersionCodeName] $colorOSFullVersion"
|
||||
/** 媒体通知自动展开仅支持 12.1 - 旧版本适配过于复杂已放弃 */
|
||||
if (colorOSNumberVersion != "V12.1") {
|
||||
binding.notifyMediaPanelAutoExpSwitch.isVisible = false
|
||||
binding.notifyMediaPanelAutoExpText.isVisible = false
|
||||
}
|
||||
binding.moduleEnableLogSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_MODULE_LOG, b)
|
||||
SystemUITool.showNeedRestartSnake(context = this)
|
||||
/** 通知面板背景透明度功能仅支持 ColorOS 12、12.1、13、13.1 */
|
||||
binding.notifyPanelConfigItem.isVisible = colorOSNumberVersion.let { it == "V12" || it == "V12.1" || it == "V13" || it == "V13.1" }
|
||||
binding.notifyIconAutoSyncText.text = ConfigData.notifyIconFixAutoTime
|
||||
binding.moduleEnableSwitch.bind(ConfigData.ENABLE_MODULE) {
|
||||
onInitialize {
|
||||
binding.moduleEnableLogItem.isVisible = it
|
||||
binding.expAllDebugLogButton.isVisible = it && ConfigData.isEnableModuleLog
|
||||
binding.notifyIconConfigItem.isVisible = it
|
||||
binding.devNotifyConfigItem.isVisible = it
|
||||
binding.notifyStyleConfigItem.isVisible = it
|
||||
}
|
||||
onChanged {
|
||||
reinitialize()
|
||||
refreshModuleStatus()
|
||||
SystemUITool.showNeedRestartSnake(context = this@MainActivity)
|
||||
}
|
||||
}
|
||||
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_HIDE_ICON, b)
|
||||
packageManager.setComponentEnabledSetting(
|
||||
ComponentName(packageName, "com.fankes.coloros.notify.Home"),
|
||||
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.DONT_KILL_APP
|
||||
)
|
||||
binding.moduleEnableLogSwitch.bind(ConfigData.ENABLE_MODULE_LOG) {
|
||||
onInitialize { binding.expAllDebugLogButton.isVisible = it && ConfigData.isEnableModule }
|
||||
onChanged {
|
||||
reinitialize()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true)
|
||||
}
|
||||
}
|
||||
binding.notifyIconFixSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX, b)
|
||||
binding.notifyIconFixButton.isVisible = b
|
||||
binding.notifyIconFixNotifyItem.isVisible = b
|
||||
binding.notifyIconAutoSyncItem.isVisible = b
|
||||
binding.devNotifyConfigSwitch.bind(ConfigData.ENABLE_REMOVE_DEV_NOTIFY) {
|
||||
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
|
||||
}
|
||||
binding.crcpNotifyConfigSwitch.bind(ConfigData.ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY) {
|
||||
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
|
||||
}
|
||||
binding.dndNotifyConfigSwitch.bind(ConfigData.ENABLE_REMOVE_DND_ALERT_NOTIFY) {
|
||||
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
|
||||
}
|
||||
binding.colorIconCompatSwitch.bind(ConfigData.ENABLE_COLOR_ICON_COMPAT) {
|
||||
isAutoApplyChanges = false
|
||||
onChanged {
|
||||
/** 应用更改并刷新系统界面 */
|
||||
fun applyChangesAndRefresh() {
|
||||
applyChanges()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
if (it) showDialog {
|
||||
title = "启用兼容模式"
|
||||
msg = "启用兼容模式可修复部分系统版本可能出现无法判定通知图标反色的问题," +
|
||||
"但是这也可能会导致新的问题,一般情况下不建议开启,确定要继续吗?\n\n" +
|
||||
"如果系统界面刷新后通知图标颜色发生错误,请尝试重启一次系统界面。"
|
||||
confirmButton { applyChangesAndRefresh() }
|
||||
cancelButton { cancelChanges() }
|
||||
noCancelable()
|
||||
} else applyChangesAndRefresh()
|
||||
}
|
||||
}
|
||||
binding.md3StyleConfigSwitch.bind(ConfigData.ENABLE_MD3_NOTIFY_ICON_STYLE) {
|
||||
onInitialize { binding.notifyIconCustomCornerItem.isVisible = it && ConfigData.isEnableNotifyIconForceAppIcon.not() }
|
||||
onChanged {
|
||||
reinitialize()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
}
|
||||
binding.notifyIconForceSystemColorSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR) {
|
||||
isAutoApplyChanges = false
|
||||
onChanged {
|
||||
/** 应用更改并刷新系统界面 */
|
||||
fun applyChangesAndRefresh() {
|
||||
applyChangesAndReinitialize()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
if (it) showDialog {
|
||||
title = "破坏性功能警告"
|
||||
msg = "开启这个功能后,任何通知栏中的通知图标都会忽略图标自身的着色属性,全部使用系统默认颜色 (系统提供的统一色调) 着色。\n\n" +
|
||||
"此功能仅面向一些追求图标美观度的用户,我们不推荐开启这个功能,且发生任何 BUG 都不会去修复,仍然继续开启吗?"
|
||||
confirmButton { applyChangesAndRefresh() }
|
||||
cancelButton { cancelChanges() }
|
||||
noCancelable()
|
||||
} else applyChangesAndRefresh()
|
||||
}
|
||||
}
|
||||
binding.notifyIconForceAppIconSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FORCE_APP_ICON) {
|
||||
isAutoApplyChanges = false
|
||||
onInitialize {
|
||||
binding.notifyIconForceSystemColorItem.isVisible = it.not()
|
||||
binding.notifyIconCustomCornerItem.isVisible = it.not() && ConfigData.isEnableMd3NotifyIconStyle
|
||||
}
|
||||
onChanged {
|
||||
/** 应用更改并刷新系统界面 */
|
||||
fun applyChangesAndRefresh() {
|
||||
applyChangesAndReinitialize()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
if (it) showDialog {
|
||||
title = "破坏性功能警告"
|
||||
msg = "开启这个功能后,任何通知栏中的通知图标都会被强制替换为当前推送通知的 APP 的图标," +
|
||||
"某些系统级别的 APP 通知图标可能会显示异常或发生图标丢失。\n\n" +
|
||||
"此功能仅面向一些追求图标美观度的用户,我们不推荐开启这个功能,且发生任何 BUG 都不会去修复,仍然继续开启吗?"
|
||||
confirmButton { applyChangesAndRefresh() }
|
||||
cancelButton { cancelChanges() }
|
||||
noCancelable()
|
||||
} else applyChangesAndRefresh()
|
||||
}
|
||||
}
|
||||
binding.notifyPanelConfigSwitch.bind(ConfigData.ENABLE_NOTIFY_PANEL_ALPHA) {
|
||||
onInitialize {
|
||||
binding.notifyPanelConfigTextPanel.isVisible = it
|
||||
binding.notifyPanelConfigWarnPanel.isVisible = it
|
||||
binding.notifyPanelConfigSeekbar.isVisible = it
|
||||
}
|
||||
onChanged {
|
||||
reinitialize()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
}
|
||||
binding.notifyMediaPanelAutoExpSwitch.bind(ConfigData.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP) {
|
||||
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
|
||||
}
|
||||
binding.notifyIconFixSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX) {
|
||||
onInitialize {
|
||||
binding.notifyIconFixButton.isVisible = it
|
||||
binding.notifyIconFixPlaceholderItem.isVisible = it
|
||||
binding.notifyIconFixNotifyItem.isVisible = it
|
||||
binding.notifyIconAutoSyncItem.isVisible = it
|
||||
}
|
||||
onChanged {
|
||||
reinitialize()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
}
|
||||
binding.notifyIconFixPlaceholderSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER) {
|
||||
isAutoApplyChanges = false
|
||||
onChanged {
|
||||
/** 应用更改并刷新系统界面 */
|
||||
fun applyChangesAndRefresh() {
|
||||
applyChanges()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
if (it) showDialog {
|
||||
title = "注意"
|
||||
msg = "开启这个功能后,当发现未适配的彩色通知图标时," +
|
||||
"状态栏中显示的通知图标将会使用预置的占位符图标进行修补," +
|
||||
"通知栏中显示的通知图标保持原始图标不变。\n\n" +
|
||||
"此功能的作用仅为临时修复破坏规范的通知图标,仍然继续开启吗?"
|
||||
confirmButton { applyChangesAndRefresh() }
|
||||
cancelButton { cancelChanges() }
|
||||
noCancelable()
|
||||
} else applyChangesAndRefresh()
|
||||
}
|
||||
}
|
||||
binding.notifyIconFixNotifySwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX_NOTIFY) {
|
||||
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
|
||||
}
|
||||
binding.notifyIconAutoSyncSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX_AUTO) {
|
||||
onInitialize { binding.notifyIconAutoSyncChildItem.isVisible = it }
|
||||
onChanged {
|
||||
reinitialize()
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true)
|
||||
}
|
||||
}
|
||||
binding.notifyPanelConfigSeekbar.bind(ConfigData.NOTIFY_PANEL_ALPHA_LEVEL, binding.notifyPanelConfigText, suffix = "%") {
|
||||
SystemUITool.refreshSystemUI(context = this)
|
||||
}
|
||||
binding.notifyIconFixNotifySwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY, b)
|
||||
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
|
||||
}
|
||||
binding.devNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.REMOVE_DEV_NOTIFY, b)
|
||||
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
|
||||
}
|
||||
binding.crcpNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.REMOVE_CHANGECP_NOTIFY, b)
|
||||
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
|
||||
}
|
||||
binding.dndNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.REMOVE_DNDALERT_NOTIFY, b)
|
||||
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
|
||||
}
|
||||
binding.a12StyleConfigSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_ANDROID12_STYLE, b)
|
||||
binding.notifyIconCustomCornerSeekbar.bind(ConfigData.NOTIFY_ICON_CORNER_SIZE, binding.notifyIconCustomCornerText, suffix = " dp") {
|
||||
SystemUITool.refreshSystemUI(context = this)
|
||||
}
|
||||
binding.notifyIconAutoSyncSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX_AUTO, b)
|
||||
binding.notifyIconAutoSyncChildItem.isVisible = b
|
||||
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
|
||||
}
|
||||
/** 导出全部日志按钮点击事件 */
|
||||
binding.expAllDebugLogButton.setOnClickListener { SystemUITool.obtainAndExportDebugLogs(context = this) }
|
||||
/** 通知图标优化名单按钮点击事件 */
|
||||
binding.notifyIconFixButton.setOnClickListener { navigate<ConfigureActivity>() }
|
||||
/** 自动更新在线规则修改时间按钮点击事件 */
|
||||
binding.notifyIconAutoSyncButton.setOnClickListener {
|
||||
showTimePicker(notifyIconAutoSyncTime) {
|
||||
showTimePicker(ConfigData.notifyIconFixAutoTime) {
|
||||
showDialog {
|
||||
title = "每天 $it 自动更新"
|
||||
msg = "设置保存后将在每天 $it 自动同步名单到最新云端数据,若数据已是最新则不会显示任何提示,否则会发送一条通知。\n\n" +
|
||||
"请确保:\n\n" +
|
||||
"1.模块没有被禁止前台以及后台联网权限\n" +
|
||||
"2.模块没有被禁止被其它 APP 关联唤醒\n" +
|
||||
"3.模块的系统通知权限已开启\n\n" +
|
||||
"模块无需保持在后台运行,到达同步时间后会自动启动,如果到达时间后模块正在运行则会自动取消本次计划任务。"
|
||||
"请确保:\n\n" +
|
||||
"1.模块没有被禁止前台以及后台联网权限\n" +
|
||||
"2.模块没有被禁止被其它 APP 关联唤醒\n" +
|
||||
"3.模块的系统通知权限已开启\n\n" +
|
||||
"模块无需保持在后台运行,到达同步时间后会自动启动,如果到达时间后模块正在运行则会自动取消本次计划任务。"
|
||||
confirmButton(text = "保存设置") {
|
||||
notifyIconAutoSyncTime = it
|
||||
binding.notifyIconAutoSyncText.text = it
|
||||
modulePrefs.put(DataConst.NOTIFY_ICON_FIX_AUTO_TIME, it)
|
||||
ConfigData.notifyIconFixAutoTime = it
|
||||
this@MainActivity.binding.notifyIconAutoSyncText.text = it
|
||||
SystemUITool.refreshSystemUI(context, isRefreshCacheOnly = true)
|
||||
}
|
||||
cancelButton()
|
||||
@@ -228,42 +342,50 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
binding.linkWithFollowMe.setOnClickListener {
|
||||
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
|
||||
}
|
||||
/** 设置桌面图标显示隐藏 */
|
||||
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
|
||||
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
hideOrShowLauncherIcon(b)
|
||||
}
|
||||
/** 注册导出调试日志启动器 */
|
||||
SystemUITool.registerExportDebugLogsLauncher(activity = this)
|
||||
}
|
||||
|
||||
/** 刷新模块状态 */
|
||||
private fun refreshModuleStatus() {
|
||||
binding.mainLinStatus.setBackgroundResource(
|
||||
when {
|
||||
isXposedModuleActive && (isModuleRegular.not() || isModuleValied.not()) -> R.drawable.bg_yellow_round
|
||||
isXposedModuleActive -> R.drawable.bg_green_round
|
||||
YukiHookAPI.Status.isXposedModuleActive &&
|
||||
(isModuleRegular.not() || isModuleValied.not() || ConfigData.isEnableModule.not()) -> R.drawable.bg_yellow_round
|
||||
YukiHookAPI.Status.isXposedModuleActive -> R.drawable.bg_green_round
|
||||
else -> R.drawable.bg_dark_round
|
||||
}
|
||||
)
|
||||
binding.mainImgStatus.setImageResource(
|
||||
when {
|
||||
isXposedModuleActive -> R.mipmap.ic_success
|
||||
else -> R.mipmap.ic_warn
|
||||
YukiHookAPI.Status.isXposedModuleActive && ConfigData.isEnableModule -> R.drawable.ic_success
|
||||
else -> R.drawable.ic_warn
|
||||
}
|
||||
)
|
||||
binding.mainTextStatus.text =
|
||||
when {
|
||||
isXposedModuleActive && isModuleRegular.not() && modulePrefs.get(DataConst.ENABLE_MODULE).not() -> "模块已停用"
|
||||
isXposedModuleActive && isModuleRegular.not() -> "模块已激活,请重启系统界面"
|
||||
isXposedModuleActive && isModuleValied.not() -> "模块已更新,请重启系统界面"
|
||||
isXposedModuleActive -> "模块已激活"
|
||||
else -> "模块未激活"
|
||||
}
|
||||
binding.mainTextApiWay.isVisible = isXposedModuleActive
|
||||
binding.mainTextApiWay.text = "Activated by ${YukiHookModuleStatus.executorName} API ${YukiHookModuleStatus.executorVersion}"
|
||||
binding.mainTextStatus.text = when {
|
||||
YukiHookAPI.Status.isXposedModuleActive && ConfigData.isEnableModule.not() -> "模块已停用"
|
||||
YukiHookAPI.Status.isXposedModuleActive && isModuleRegular.not() -> "模块已激活,请重启系统界面"
|
||||
YukiHookAPI.Status.isXposedModuleActive && isModuleValied.not() -> "模块已更新,请重启系统界面"
|
||||
YukiHookAPI.Status.isXposedModuleActive -> "模块已激活"
|
||||
else -> "模块未激活"
|
||||
}
|
||||
binding.mainTextApiWay.isVisible = YukiHookAPI.Status.isXposedModuleActive
|
||||
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
/** 刷新模块状态 */
|
||||
refreshModuleStatus()
|
||||
/** 发送广播检查模块激活状态 */
|
||||
SystemUITool.checkingActivated(context = this) { isRegular, isValied ->
|
||||
isModuleRegular = isRegular
|
||||
/** 检查模块激活状态 */
|
||||
SystemUITool.checkingActivated(context = this) { isValied ->
|
||||
isModuleRegular = true
|
||||
isModuleValied = isValied
|
||||
refreshModuleStatus()
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,7 +18,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/26.
|
||||
* This file is created by fankes on 2022/3/26.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext
|
||||
import com.fankes.coloros.notify.ui.activity.base.BaseActivity
|
||||
import com.fankes.coloros.notify.utils.factory.delayedRun
|
||||
import com.fankes.coloros.notify.utils.tool.IconRuleManagerTool
|
||||
@@ -48,10 +47,8 @@ class NotifyIconRuleUpdateActivity : Activity() {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
/** 注册 */
|
||||
SystemUITool.register(context = this)
|
||||
/** 拉取云端数据 */
|
||||
IconRuleManagerTool.sync(appContext) {
|
||||
IconRuleManagerTool.sync(context = this) {
|
||||
/** 刷新系统界面 */
|
||||
SystemUITool.refreshSystemUI()
|
||||
/** 结束当前窗口 */
|
||||
@@ -60,10 +57,4 @@ class NotifyIconRuleUpdateActivity : Activity() {
|
||||
/** 切换到后台 */
|
||||
moveTaskToBack(true)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
/** 解除注册 */
|
||||
SystemUITool.unregister(context = this)
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,22 +18,22 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/30.
|
||||
* This file is created by fankes on 2022/1/30.
|
||||
*/
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package com.fankes.coloros.notify.ui.activity.base
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.fankes.coloros.notify.R
|
||||
import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode
|
||||
import com.fankes.coloros.notify.utils.tool.SystemUITool
|
||||
import com.gyf.immersionbar.ktx.immersionBar
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import com.highcapable.kavaref.KavaRef.Companion.resolve
|
||||
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
|
||||
import com.highcapable.kavaref.extension.toClassOrNull
|
||||
|
||||
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
@@ -49,38 +49,30 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
isMainThreadRunning = true
|
||||
javaClass.genericSuperclass.also { type ->
|
||||
if (type is ParameterizedType) {
|
||||
binding = (type.actualTypeArguments[0] as Class<VB>).method {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}.get().invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
setContentView(binding.root)
|
||||
} else error("binding but got wrong type")
|
||||
}
|
||||
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
|
||||
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
|
||||
name = "inflate"
|
||||
parameters(LayoutInflater::class)
|
||||
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
|
||||
setContentView(binding.root)
|
||||
/** 隐藏系统的标题栏 */
|
||||
supportActionBar?.hide()
|
||||
/** 初始化沉浸状态栏 */
|
||||
immersionBar {
|
||||
statusBarColor(R.color.colorThemeBackground)
|
||||
autoDarkModeEnable(true)
|
||||
statusBarDarkFont(isNotSystemInDarkMode)
|
||||
navigationBarColor(R.color.colorThemeBackground)
|
||||
navigationBarDarkIcon(isNotSystemInDarkMode)
|
||||
fitsSystemWindows(true)
|
||||
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||
isAppearanceLightStatusBars = isNotSystemInDarkMode
|
||||
isAppearanceLightNavigationBars = isNotSystemInDarkMode
|
||||
}
|
||||
@Suppress("DEPRECATION")
|
||||
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
|
||||
window?.statusBarColor = it
|
||||
window?.navigationBarColor = it
|
||||
window?.navigationBarDividerColor = it
|
||||
}
|
||||
/** 注册 */
|
||||
SystemUITool.register(context = this)
|
||||
/** 装载子类 */
|
||||
onCreate()
|
||||
}
|
||||
|
||||
/** 回调 [onCreate] 方法 */
|
||||
abstract fun onCreate()
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
/** 解除注册 */
|
||||
SystemUITool.unregister(context = this)
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,11 +18,11 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
* This file is created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
||||
package com.fankes.coloros.notify.ui.view
|
||||
package com.fankes.coloros.notify.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
@@ -30,9 +30,9 @@ import android.graphics.Color
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import com.fankes.coloros.notify.utils.drawable.drawabletoolbox.DrawableBuilder
|
||||
import com.fankes.coloros.notify.utils.factory.dp
|
||||
import com.fankes.coloros.notify.utils.factory.isSystemInDarkMode
|
||||
import top.defaults.drawabletoolbox.DrawableBuilder
|
||||
|
||||
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {
|
||||
|
@@ -1,293 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.RippleDrawable
|
||||
import android.graphics.drawable.RotateDrawable
|
||||
import android.os.Build
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
|
||||
private val gradientState = resolveGradientState()
|
||||
|
||||
private fun resolveGradientState(): Class<*> {
|
||||
val classes = GradientDrawable::class.java.declaredClasses
|
||||
for (singleClass in classes) {
|
||||
if (singleClass.simpleName == "GradientState") return singleClass
|
||||
}
|
||||
throw RuntimeException("GradientState could not be found in thisAny GradientDrawable implementation")
|
||||
}
|
||||
|
||||
private val rotateState = resolveRotateState()
|
||||
|
||||
private fun resolveRotateState(): Class<*> {
|
||||
val classes = RotateDrawable::class.java.declaredClasses
|
||||
for (singleClass in classes) {
|
||||
if (singleClass.simpleName == "RotateState") return singleClass
|
||||
}
|
||||
throw RuntimeException("RotateState could not be found in thisAny RotateDrawable implementation")
|
||||
}
|
||||
|
||||
@Throws(SecurityException::class, NoSuchFieldException::class)
|
||||
private fun resolveField(source: Class<*>, fieldName: String): Field {
|
||||
val field = source.getDeclaredField(fieldName)
|
||||
field.isAccessible = true
|
||||
return field
|
||||
}
|
||||
|
||||
@Throws(SecurityException::class, NoSuchMethodException::class)
|
||||
private fun resolveMethod(
|
||||
source: Class<*>,
|
||||
methodName: String,
|
||||
vararg parameterTypes: Class<*>
|
||||
): Method {
|
||||
val method = source.getDeclaredMethod(methodName, *parameterTypes)
|
||||
method.isAccessible = true
|
||||
return method
|
||||
}
|
||||
|
||||
fun setInnerRadius(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mInnerRadius")
|
||||
innerRadius.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setInnerRadiusRatio(drawable: GradientDrawable, value: Float) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mInnerRadiusRatio")
|
||||
innerRadius.setFloat(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setThickness(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mThickness")
|
||||
innerRadius.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setThicknessRatio(drawable: GradientDrawable, value: Float) {
|
||||
try {
|
||||
val innerRadius = resolveField(gradientState, "mThicknessRatio")
|
||||
innerRadius.setFloat(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setUseLevelForShape(drawable: GradientDrawable, value: Boolean) {
|
||||
try {
|
||||
val useLevelForShape = resolveField(gradientState, "mUseLevelForShape")
|
||||
useLevelForShape.setBoolean(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
fun setOrientation(drawable: GradientDrawable, value: GradientDrawable.Orientation) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
drawable.orientation = value
|
||||
} else {
|
||||
try {
|
||||
val orientation = resolveField(gradientState, "mOrientation")
|
||||
orientation.set(drawable.constantState, value)
|
||||
val rectIdDirty = resolveField(GradientDrawable::class.java, "mRectIsDirty")
|
||||
rectIdDirty.setBoolean(drawable, true)
|
||||
drawable.invalidateSelf()
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
fun setColors(drawable: GradientDrawable, value: IntArray) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
drawable.colors = value
|
||||
} else {
|
||||
try {
|
||||
val colors = resolveField(gradientState, "mColors")
|
||||
colors.set(drawable.constantState, value)
|
||||
drawable.invalidateSelf()
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setGradientRadiusType(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val type = resolveField(gradientState, "mGradientRadiusType")
|
||||
type.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setGradientRadius(drawable: GradientDrawable, value: Float) {
|
||||
try {
|
||||
val gradientRadius = resolveField(gradientState, "mGradientRadius")
|
||||
gradientRadius.setFloat(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setStrokeColor(drawable: GradientDrawable, value: Int) {
|
||||
try {
|
||||
val type = resolveField(gradientState, "mStrokeColor")
|
||||
type.setInt(drawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun setDrawable(rotateDrawable: RotateDrawable, drawable: Drawable) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.drawable = drawable
|
||||
} else {
|
||||
try {
|
||||
val drawableField = resolveField(rotateState, "mDrawable")
|
||||
val stateField = resolveField(RotateDrawable::class.java, "mState")
|
||||
drawableField.set(stateField.get(rotateDrawable), drawable)
|
||||
drawable.callback = rotateDrawable
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setPivotX(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.pivotX = value
|
||||
} else {
|
||||
try {
|
||||
val pivotXField = resolveField(rotateState, "mPivotX")
|
||||
pivotXField.setFloat(rotateDrawable.constantState, value)
|
||||
val pivotXRelField = resolveField(rotateState, "mPivotXRel")
|
||||
pivotXRelField.setBoolean(rotateDrawable.constantState, true)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setPivotY(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.pivotY = value
|
||||
} else {
|
||||
try {
|
||||
val pivotYField = resolveField(rotateState, "mPivotY")
|
||||
pivotYField.setFloat(rotateDrawable.constantState, value)
|
||||
val pivotYRelField = resolveField(rotateState, "mPivotYRel")
|
||||
pivotYRelField.setBoolean(rotateDrawable.constantState, true)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setFromDegrees(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.fromDegrees = value
|
||||
} else {
|
||||
try {
|
||||
val fromDegreesField = resolveField(rotateState, "mFromDegrees")
|
||||
fromDegreesField.setFloat(rotateDrawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setToDegrees(rotateDrawable: RotateDrawable, value: Float) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
rotateDrawable.toDegrees = value
|
||||
} else {
|
||||
try {
|
||||
val toDegreesField = resolveField(rotateState, "mToDegrees")
|
||||
toDegreesField.setFloat(rotateDrawable.constantState, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setRadius(rippleDrawable: RippleDrawable, value: Int) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
rippleDrawable.radius = value
|
||||
} else {
|
||||
try {
|
||||
val setRadiusMethod =
|
||||
resolveMethod(RippleDrawable::class.java, "setMaxRadius", Int::class.java)
|
||||
setRadiusMethod.invoke(rippleDrawable, value)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
class Constants {
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_COLOR = 0xFFBA68C8.toInt()
|
||||
}
|
||||
}
|
@@ -1,490 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.util.StateSet
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class DrawableBuilder {
|
||||
|
||||
private var properties = DrawableProperties()
|
||||
private var order: AtomicInteger = AtomicInteger(1)
|
||||
private var transformsMap = TreeMap<Int, (Drawable) -> Drawable>()
|
||||
private var baseDrawable: Drawable? = null
|
||||
|
||||
fun batch(properties: DrawableProperties) = apply { this.properties = properties.copy() }
|
||||
fun baseDrawable(drawable: Drawable) = apply { baseDrawable = drawable }
|
||||
|
||||
// <shape>
|
||||
fun shape(shape: Int) = apply { properties.shape = shape }
|
||||
|
||||
fun rectangle() = apply { shape(GradientDrawable.RECTANGLE) }
|
||||
fun oval() = apply { shape(GradientDrawable.OVAL) }
|
||||
fun line() = apply { shape(GradientDrawable.LINE) }
|
||||
fun ring() = apply { shape(GradientDrawable.RING) }
|
||||
fun innerRadius(innerRadius: Int) = apply { properties.innerRadius = innerRadius }
|
||||
fun innerRadiusRatio(innerRadiusRatio: Float) =
|
||||
apply { properties.innerRadiusRatio = innerRadiusRatio }
|
||||
|
||||
fun thickness(thickness: Int) = apply { properties.thickness = thickness }
|
||||
fun thicknessRatio(thicknessRatio: Float) = apply { properties.thicknessRatio = thicknessRatio }
|
||||
|
||||
fun useLevelForRing(use: Boolean = true) = apply { properties.useLevelForRing = use }
|
||||
|
||||
// <corner>
|
||||
fun cornerRadius(cornerRadius: Int) = apply { properties.cornerRadius = cornerRadius }
|
||||
|
||||
fun topLeftRadius(topLeftRadius: Int) = apply { properties.topLeftRadius = topLeftRadius }
|
||||
fun topRightRadius(topRightRadius: Int) = apply { properties.topRightRadius = topRightRadius }
|
||||
fun bottomRightRadius(bottomRightRadius: Int) =
|
||||
apply { properties.bottomRightRadius = bottomRightRadius }
|
||||
|
||||
fun bottomLeftRadius(bottomLeftRadius: Int) =
|
||||
apply { properties.bottomLeftRadius = bottomLeftRadius }
|
||||
|
||||
fun rounded() = apply { cornerRadius(Int.MAX_VALUE) }
|
||||
fun cornerRadii(
|
||||
topLeftRadius: Int,
|
||||
topRightRadius: Int,
|
||||
bottomRightRadius: Int,
|
||||
bottomLeftRadius: Int
|
||||
) = apply {
|
||||
topLeftRadius(topLeftRadius); topRightRadius(topRightRadius); bottomRightRadius(
|
||||
bottomRightRadius
|
||||
); bottomLeftRadius(bottomLeftRadius)
|
||||
}
|
||||
|
||||
// <gradient>
|
||||
|
||||
fun gradient(useGradient: Boolean = true) = apply { properties.useGradient = useGradient }
|
||||
|
||||
fun gradientType(type: Int) = apply { properties.type = type }
|
||||
fun linearGradient() = apply { gradientType(GradientDrawable.LINEAR_GRADIENT) }
|
||||
fun radialGradient() = apply { gradientType(GradientDrawable.RADIAL_GRADIENT) }
|
||||
fun sweepGradient() = apply { gradientType(GradientDrawable.SWEEP_GRADIENT) }
|
||||
fun angle(angle: Int) = apply { properties.angle = angle }
|
||||
fun centerX(centerX: Float) = apply { properties.centerX = centerX }
|
||||
fun centerY(centerY: Float) = apply { properties.centerY = centerY }
|
||||
fun center(centerX: Float, centerY: Float) = apply { centerX(centerX); centerY(centerY) }
|
||||
|
||||
fun useCenterColor(useCenterColor: Boolean = true) =
|
||||
apply { properties.useCenterColor = useCenterColor }
|
||||
|
||||
fun startColor(startColor: Int) = apply { properties.startColor = startColor }
|
||||
fun centerColor(centerColor: Int) = apply {
|
||||
properties.centerColor = centerColor
|
||||
useCenterColor(true)
|
||||
}
|
||||
|
||||
fun endColor(endColor: Int) = apply { properties.endColor = endColor }
|
||||
fun gradientColors(startColor: Int, endColor: Int, centerColor: Int?) = apply {
|
||||
startColor(startColor); endColor(endColor)
|
||||
useCenterColor(centerColor != null)
|
||||
centerColor?.let {
|
||||
centerColor(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun gradientRadiusType(gradientRadiusType: Int) =
|
||||
apply { properties.gradientRadiusType = gradientRadiusType }
|
||||
|
||||
fun gradientRadius(gradientRadius: Float) = apply { properties.gradientRadius = gradientRadius }
|
||||
fun gradientRadius(radius: Float, type: Int) =
|
||||
apply { gradientRadius(radius); gradientRadiusType(type) }
|
||||
|
||||
fun gradientRadiusInPixel(radius: Float) =
|
||||
apply { gradientRadius(radius); gradientRadiusType(DrawableProperties.RADIUS_TYPE_PIXELS) }
|
||||
|
||||
fun gradientRadiusInFraction(radius: Float) =
|
||||
apply { gradientRadius(radius); gradientRadiusType(DrawableProperties.RADIUS_TYPE_FRACTION) }
|
||||
|
||||
fun useLevelForGradient(use: Boolean) = apply { properties.useLevelForGradient = use }
|
||||
fun useLevelForGradient() = apply { useLevelForGradient(true) }
|
||||
|
||||
// <size>
|
||||
fun width(width: Int) = apply { properties.width = width }
|
||||
|
||||
fun height(height: Int) = apply { properties.height = height }
|
||||
fun size(width: Int, height: Int) = apply { width(width); height(height) }
|
||||
fun size(size: Int) = apply { width(size).height(size) }
|
||||
|
||||
// <solid>
|
||||
fun solidColor(solidColor: Int) = apply { properties.solidColor = solidColor }
|
||||
|
||||
private var solidColorPressed: Int? = null
|
||||
fun solidColorPressed(color: Int?) = apply { solidColorPressed = color }
|
||||
private var solidColorPressedWhenRippleUnsupported: Int? = null
|
||||
fun solidColorPressedWhenRippleUnsupported(color: Int?) =
|
||||
apply { solidColorPressedWhenRippleUnsupported = color }
|
||||
|
||||
private var solidColorDisabled: Int? = null
|
||||
fun solidColorDisabled(color: Int?) = apply { solidColorDisabled = color }
|
||||
private var solidColorSelected: Int? = null
|
||||
fun solidColorSelected(color: Int?) = apply { solidColorSelected = color }
|
||||
fun solidColorStateList(colorStateList: ColorStateList) =
|
||||
apply { properties.solidColorStateList = colorStateList }
|
||||
|
||||
// <stroke>
|
||||
fun strokeWidth(strokeWidth: Int) = apply { properties.strokeWidth = strokeWidth }
|
||||
|
||||
fun strokeColor(strokeColor: Int) = apply { properties.strokeColor = strokeColor }
|
||||
private var strokeColorPressed: Int? = null
|
||||
fun strokeColorPressed(color: Int?) = apply { strokeColorPressed = color }
|
||||
private var strokeColorDisabled: Int? = null
|
||||
fun strokeColorDisabled(color: Int?) = apply { strokeColorDisabled = color }
|
||||
private var strokeColorSelected: Int? = null
|
||||
fun strokeColorSelected(color: Int?) = apply { strokeColorSelected = color }
|
||||
fun strokeColorStateList(colorStateList: ColorStateList) =
|
||||
apply { properties.strokeColorStateList = colorStateList }
|
||||
|
||||
fun dashWidth(dashWidth: Int) = apply { properties.dashWidth = dashWidth }
|
||||
fun dashGap(dashGap: Int) = apply { properties.dashGap = dashGap }
|
||||
fun hairlineBordered() = apply { strokeWidth(1) }
|
||||
fun shortDashed() = apply { dashWidth(12).dashGap(12) }
|
||||
fun mediumDashed() = apply { dashWidth(24).dashGap(24) }
|
||||
fun longDashed() = apply { dashWidth(36).dashGap(36) }
|
||||
fun dashed() = apply { mediumDashed() }
|
||||
|
||||
// <rotate>
|
||||
private var rotateOrder = 0
|
||||
|
||||
|
||||
fun rotate(boolean: Boolean = true) = apply {
|
||||
properties.useRotate = boolean
|
||||
rotateOrder = if (boolean) {
|
||||
order.getAndIncrement()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fun pivotX(pivotX: Float) = apply { properties.pivotX = pivotX }
|
||||
fun pivotY(pivotY: Float) = apply { properties.pivotY = pivotY }
|
||||
fun pivot(pivotX: Float, pivotY: Float) = apply { pivotX(pivotX).pivotY(pivotY) }
|
||||
fun fromDegrees(degrees: Float) = apply { properties.fromDegrees = degrees }
|
||||
fun toDegrees(degrees: Float) = apply { properties.toDegrees = degrees }
|
||||
fun degrees(fromDegrees: Float, toDegrees: Float) =
|
||||
apply { fromDegrees(fromDegrees).toDegrees(toDegrees) }
|
||||
|
||||
fun degrees(degrees: Float) = apply { fromDegrees(degrees).toDegrees(degrees) }
|
||||
fun rotate(fromDegrees: Float, toDegrees: Float) =
|
||||
apply { rotate().fromDegrees(fromDegrees).toDegrees(toDegrees) }
|
||||
|
||||
fun rotate(degrees: Float) = apply { rotate().degrees(degrees) }
|
||||
|
||||
// <scale>
|
||||
private var scaleOrder = 0
|
||||
|
||||
|
||||
fun scale(boolean: Boolean = true) = apply {
|
||||
properties.useScale = boolean
|
||||
scaleOrder = if (boolean) {
|
||||
order.getAndIncrement()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fun scaleLevel(level: Int) = apply { properties.scaleLevel = level }
|
||||
fun scaleGravity(gravity: Int) = apply { properties.scaleGravity = gravity }
|
||||
fun scaleWidth(scale: Float) = apply { properties.scaleWidth = scale }
|
||||
fun scaleHeight(scale: Float) = apply { properties.scaleHeight = scale }
|
||||
fun scale(scale: Float) = apply { scale().scaleWidth(scale).scaleHeight(scale) }
|
||||
fun scale(scaleWidth: Float, scaleHeight: Float) =
|
||||
apply { scale().scaleWidth(scaleWidth).scaleHeight(scaleHeight) }
|
||||
|
||||
// flip
|
||||
|
||||
fun flip(boolean: Boolean = true) = apply { properties.useFlip = boolean }
|
||||
|
||||
fun orientation(orientation: Int) = apply { properties.orientation = orientation }
|
||||
fun flipVertical() = apply { flip().orientation(FlipDrawable.ORIENTATION_VERTICAL) }
|
||||
|
||||
// <ripple>
|
||||
|
||||
fun ripple(boolean: Boolean = true) = apply { properties.useRipple = boolean }
|
||||
|
||||
fun rippleColor(color: Int) = apply { properties.rippleColor = color }
|
||||
fun rippleColorStateList(colorStateList: ColorStateList) =
|
||||
apply { properties.rippleColorStateList = colorStateList }
|
||||
|
||||
fun rippleRadius(radius: Int) = apply { properties.rippleRadius = radius }
|
||||
|
||||
fun build(): Drawable {
|
||||
if (baseDrawable != null) {
|
||||
return wrap(baseDrawable!!)
|
||||
}
|
||||
|
||||
var drawable: Drawable
|
||||
|
||||
// fall back when ripple is unavailable on devices with API < 21
|
||||
if (shouldFallbackRipple()) {
|
||||
if (solidColorPressedWhenRippleUnsupported != null) {
|
||||
solidColorPressed(solidColorPressedWhenRippleUnsupported)
|
||||
} else {
|
||||
solidColorPressed(properties.rippleColor)
|
||||
}
|
||||
}
|
||||
|
||||
if (needStateListDrawable()) {
|
||||
drawable = StateListDrawableBuilder()
|
||||
.pressed(buildPressedDrawable())
|
||||
.disabled(buildDisabledDrawable())
|
||||
.selected(buildSelectedDrawable())
|
||||
.normal(buildNormalDrawable())
|
||||
.build()
|
||||
} else {
|
||||
drawable = GradientDrawable()
|
||||
setupGradientDrawable(drawable)
|
||||
}
|
||||
drawable = wrap(drawable)
|
||||
return drawable
|
||||
}
|
||||
|
||||
private fun getSolidColorStateList(): ColorStateList {
|
||||
if (properties.solidColorStateList != null) {
|
||||
return properties.solidColorStateList!!
|
||||
}
|
||||
|
||||
val states = mutableListOf<IntArray>()
|
||||
val colors = mutableListOf<Int>()
|
||||
|
||||
solidColorPressed?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_pressed))
|
||||
colors.add(it)
|
||||
}
|
||||
solidColorDisabled?.let {
|
||||
states.add(intArrayOf(-android.R.attr.state_enabled))
|
||||
colors.add(it)
|
||||
}
|
||||
solidColorSelected?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_selected))
|
||||
colors.add(it)
|
||||
}
|
||||
states.add(StateSet.WILD_CARD)
|
||||
colors.add(properties.solidColor)
|
||||
|
||||
return ColorStateList(states.toTypedArray(), colors.toIntArray())
|
||||
}
|
||||
|
||||
private fun getStrokeColorStateList(): ColorStateList {
|
||||
if (properties.strokeColorStateList != null) {
|
||||
return properties.strokeColorStateList!!
|
||||
}
|
||||
|
||||
val states = mutableListOf<IntArray>()
|
||||
val colors = mutableListOf<Int>()
|
||||
|
||||
strokeColorPressed?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_pressed))
|
||||
colors.add(it)
|
||||
}
|
||||
strokeColorDisabled?.let {
|
||||
states.add(intArrayOf(-android.R.attr.state_enabled))
|
||||
colors.add(it)
|
||||
}
|
||||
strokeColorSelected?.let {
|
||||
states.add(intArrayOf(android.R.attr.state_selected))
|
||||
colors.add(it)
|
||||
}
|
||||
states.add(StateSet.WILD_CARD)
|
||||
colors.add(properties.strokeColor)
|
||||
|
||||
return ColorStateList(states.toTypedArray(), colors.toIntArray())
|
||||
}
|
||||
|
||||
private fun buildPressedDrawable(): Drawable? {
|
||||
if (solidColorPressed == null && strokeColorPressed == null) return null
|
||||
|
||||
val pressedDrawable = GradientDrawable()
|
||||
setupGradientDrawable(pressedDrawable)
|
||||
solidColorPressed?.let {
|
||||
pressedDrawable.setColor(it)
|
||||
}
|
||||
strokeColorPressed?.let {
|
||||
setStrokeColor(pressedDrawable, it)
|
||||
}
|
||||
return pressedDrawable
|
||||
}
|
||||
|
||||
private fun buildDisabledDrawable(): Drawable? {
|
||||
if (solidColorDisabled == null && strokeColorDisabled == null) return null
|
||||
|
||||
val disabledDrawable = GradientDrawable()
|
||||
setupGradientDrawable(disabledDrawable)
|
||||
solidColorDisabled?.let {
|
||||
disabledDrawable.setColor(it)
|
||||
}
|
||||
strokeColorDisabled?.let {
|
||||
setStrokeColor(disabledDrawable, it)
|
||||
}
|
||||
return disabledDrawable
|
||||
}
|
||||
|
||||
private fun buildSelectedDrawable(): Drawable? {
|
||||
if (solidColorSelected == null && strokeColorSelected == null) return null
|
||||
|
||||
val selectedDrawable = GradientDrawable()
|
||||
setupGradientDrawable(selectedDrawable)
|
||||
solidColorSelected?.let {
|
||||
selectedDrawable.setColor(it)
|
||||
}
|
||||
strokeColorSelected?.let {
|
||||
setStrokeColor(selectedDrawable, it)
|
||||
}
|
||||
return selectedDrawable
|
||||
}
|
||||
|
||||
private fun buildNormalDrawable(): Drawable {
|
||||
val pressedDrawable = GradientDrawable()
|
||||
setupGradientDrawable(pressedDrawable)
|
||||
return pressedDrawable
|
||||
}
|
||||
|
||||
private fun setupGradientDrawable(drawable: GradientDrawable) {
|
||||
properties.apply {
|
||||
drawable.shape = shape
|
||||
if (shape == GradientDrawable.RING) {
|
||||
setInnerRadius(drawable, innerRadius)
|
||||
setInnerRadiusRatio(drawable, innerRadiusRatio)
|
||||
setThickness(drawable, thickness)
|
||||
setThicknessRatio(drawable, thicknessRatio)
|
||||
setUseLevelForShape(drawable, useLevelForRing)
|
||||
}
|
||||
drawable.cornerRadii = getCornerRadii()
|
||||
if (useGradient) {
|
||||
drawable.gradientType = type
|
||||
setGradientRadiusType(drawable, gradientRadiusType)
|
||||
setGradientRadius(drawable, gradientRadius)
|
||||
drawable.setGradientCenter(centerX, centerY)
|
||||
setOrientation(drawable, getOrientation())
|
||||
setColors(drawable, getColors())
|
||||
drawable.useLevel = useLevelForGradient
|
||||
} else {
|
||||
drawable.color = getSolidColorStateList()
|
||||
}
|
||||
drawable.setSize(width, height)
|
||||
drawable.setStroke(
|
||||
strokeWidth,
|
||||
getStrokeColorStateList(),
|
||||
dashWidth.toFloat(),
|
||||
dashGap.toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun needStateListDrawable(): Boolean {
|
||||
return (hasStrokeColorStateList() || (!properties.useGradient && hasSolidColorStateList()))
|
||||
}
|
||||
|
||||
private fun needRotateDrawable(): Boolean {
|
||||
return properties.useRotate &&
|
||||
!(properties.pivotX == 0.5f && properties.pivotY == 0.5f
|
||||
&& properties.fromDegrees == 0f && properties.toDegrees == 0f)
|
||||
}
|
||||
|
||||
private fun needScaleDrawable(): Boolean {
|
||||
return properties.useScale
|
||||
}
|
||||
|
||||
private fun wrap(drawable: Drawable): Drawable {
|
||||
var wrappedDrawable = drawable
|
||||
|
||||
if (rotateOrder > 0) {
|
||||
transformsMap[rotateOrder] = ::wrapRotateIfNeeded
|
||||
}
|
||||
if (scaleOrder > 0) {
|
||||
transformsMap[scaleOrder] = ::wrapScaleIfNeeded
|
||||
}
|
||||
|
||||
for (action in transformsMap.values) {
|
||||
wrappedDrawable = action.invoke(wrappedDrawable)
|
||||
}
|
||||
|
||||
if (properties.useFlip) {
|
||||
wrappedDrawable = FlipDrawableBuilder()
|
||||
.drawable(wrappedDrawable)
|
||||
.orientation(properties.orientation)
|
||||
.build()
|
||||
}
|
||||
|
||||
if (isRippleSupported() && properties.useRipple) {
|
||||
wrappedDrawable = RippleDrawableBuilder()
|
||||
.drawable(wrappedDrawable)
|
||||
.color(properties.rippleColor)
|
||||
.colorStateList(properties.rippleColorStateList)
|
||||
.radius(properties.rippleRadius)
|
||||
.build()
|
||||
}
|
||||
|
||||
return wrappedDrawable
|
||||
}
|
||||
|
||||
private fun shouldFallbackRipple(): Boolean {
|
||||
return properties.useRipple && !isRippleSupported()
|
||||
}
|
||||
|
||||
private fun isRippleSupported() = true
|
||||
|
||||
private fun wrapRotateIfNeeded(drawable: Drawable): Drawable {
|
||||
if (!needRotateDrawable()) return drawable
|
||||
|
||||
with(properties) {
|
||||
return RotateDrawableBuilder()
|
||||
.drawable(drawable)
|
||||
.pivotX(pivotX)
|
||||
.pivotY(pivotY)
|
||||
.fromDegrees(fromDegrees)
|
||||
.toDegrees(toDegrees)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapScaleIfNeeded(drawable: Drawable): Drawable {
|
||||
if (!needScaleDrawable()) return drawable
|
||||
|
||||
with(properties) {
|
||||
return ScaleDrawableBuilder()
|
||||
.drawable(drawable)
|
||||
.level(scaleLevel)
|
||||
.scaleGravity(scaleGravity)
|
||||
.scaleWidth(scaleWidth)
|
||||
.scaleHeight(scaleHeight)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasSolidColorStateList(): Boolean {
|
||||
return solidColorPressed != null || solidColorDisabled != null || solidColorSelected != null
|
||||
}
|
||||
|
||||
private fun hasStrokeColorStateList(): Boolean {
|
||||
return strokeColorPressed != null || strokeColorDisabled != null || strokeColorSelected != null
|
||||
}
|
||||
}
|
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("SetterBackingFieldAssignment", "unused")
|
||||
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.view.Gravity
|
||||
import java.io.Serializable
|
||||
|
||||
data class DrawableProperties(
|
||||
|
||||
// <shape>
|
||||
@JvmField var shape: Int = GradientDrawable.RECTANGLE,
|
||||
@JvmField var innerRadius: Int = -1,
|
||||
@JvmField var innerRadiusRatio: Float = 9f,
|
||||
@JvmField var thickness: Int = -1,
|
||||
@JvmField var thicknessRatio: Float = 3f,
|
||||
@JvmField var useLevelForRing: Boolean = false,
|
||||
|
||||
// <corner>
|
||||
private var _cornerRadius: Int = 0,
|
||||
@JvmField var topLeftRadius: Int = 0,
|
||||
@JvmField var topRightRadius: Int = 0,
|
||||
@JvmField var bottomRightRadius: Int = 0,
|
||||
@JvmField var bottomLeftRadius: Int = 0,
|
||||
|
||||
// <gradient>
|
||||
@JvmField var useGradient: Boolean = false,
|
||||
@JvmField var type: Int = GradientDrawable.RADIAL_GRADIENT,
|
||||
@JvmField var angle: Int = 0,
|
||||
@JvmField var centerX: Float = 0.5f,
|
||||
@JvmField var centerY: Float = 0.5f,
|
||||
@JvmField var useCenterColor: Boolean = false,
|
||||
@JvmField var startColor: Int = Constants.DEFAULT_COLOR,
|
||||
@JvmField var centerColor: Int? = null,
|
||||
@JvmField var endColor: Int = 0x7FFFFFFF,
|
||||
@JvmField var gradientRadiusType: Int = RADIUS_TYPE_FRACTION,
|
||||
@JvmField var gradientRadius: Float = 0.5f,
|
||||
@JvmField var useLevelForGradient: Boolean = false,
|
||||
|
||||
// <size>
|
||||
@JvmField var width: Int = -1,
|
||||
@JvmField var height: Int = -1,
|
||||
|
||||
// <solid>
|
||||
@JvmField var solidColor: Int = Color.TRANSPARENT,
|
||||
@JvmField var solidColorStateList: ColorStateList? = null,
|
||||
|
||||
// <stroke>
|
||||
@JvmField var strokeWidth: Int = 0,
|
||||
@JvmField var strokeColor: Int = Color.DKGRAY,
|
||||
@JvmField var strokeColorStateList: ColorStateList? = null,
|
||||
@JvmField var dashWidth: Int = 0,
|
||||
@JvmField var dashGap: Int = 0,
|
||||
|
||||
// <rotate>
|
||||
@JvmField var useRotate: Boolean = false,
|
||||
@JvmField var pivotX: Float = 0.5f,
|
||||
@JvmField var pivotY: Float = 0.5f,
|
||||
@JvmField var fromDegrees: Float = 0f,
|
||||
@JvmField var toDegrees: Float = 0f,
|
||||
|
||||
// <scale>
|
||||
@JvmField var useScale: Boolean = false,
|
||||
@JvmField var scaleLevel: Int = 10000,
|
||||
@JvmField var scaleGravity: Int = Gravity.CENTER,
|
||||
@JvmField var scaleWidth: Float = 0f,
|
||||
@JvmField var scaleHeight: Float = 0f,
|
||||
|
||||
// flip
|
||||
@JvmField var useFlip: Boolean = false,
|
||||
@JvmField var orientation: Int = FlipDrawable.ORIENTATION_HORIZONTAL,
|
||||
|
||||
// ripple
|
||||
@JvmField var useRipple: Boolean = false,
|
||||
@JvmField var rippleColor: Int = Constants.DEFAULT_COLOR,
|
||||
@JvmField var rippleColorStateList: ColorStateList? = null,
|
||||
@JvmField var rippleRadius: Int = -1
|
||||
) : Serializable {
|
||||
|
||||
companion object {
|
||||
const val RADIUS_TYPE_PIXELS = 0
|
||||
const val RADIUS_TYPE_FRACTION = 1
|
||||
|
||||
@JvmField
|
||||
val CREATOR = object : Parcelable.Creator<DrawableProperties> {
|
||||
override fun createFromParcel(parcel: Parcel): DrawableProperties {
|
||||
return DrawableProperties(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<DrawableProperties?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cornerRadius: Int = _cornerRadius
|
||||
set(value) {
|
||||
_cornerRadius = value
|
||||
topLeftRadius = value
|
||||
topRightRadius = value
|
||||
bottomRightRadius = value
|
||||
bottomLeftRadius = value
|
||||
}
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readParcelable(ColorStateList::class.java.classLoader),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readParcelable(ColorStateList::class.java.classLoader),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readFloat(),
|
||||
parcel.readFloat(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readByte() != 0.toByte(),
|
||||
parcel.readInt(),
|
||||
parcel.readParcelable(ColorStateList::class.java.classLoader),
|
||||
parcel.readInt()
|
||||
)
|
||||
|
||||
fun copy(): DrawableProperties {
|
||||
val parcel = Parcel.obtain()
|
||||
parcel.setDataPosition(0)
|
||||
val properties = CREATOR.createFromParcel(parcel)
|
||||
parcel.recycle()
|
||||
return properties
|
||||
}
|
||||
|
||||
fun getCornerRadii(): FloatArray {
|
||||
return floatArrayOf(
|
||||
topLeftRadius.toFloat(), topLeftRadius.toFloat(),
|
||||
topRightRadius.toFloat(), topRightRadius.toFloat(),
|
||||
bottomRightRadius.toFloat(), bottomRightRadius.toFloat(),
|
||||
bottomLeftRadius.toFloat(), bottomLeftRadius.toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
fun getOrientation(): GradientDrawable.Orientation {
|
||||
val orientation: GradientDrawable.Orientation = when (val angle = this.angle % 360) {
|
||||
0 -> GradientDrawable.Orientation.LEFT_RIGHT
|
||||
45 -> GradientDrawable.Orientation.BL_TR
|
||||
90 -> GradientDrawable.Orientation.BOTTOM_TOP
|
||||
135 -> GradientDrawable.Orientation.BR_TL
|
||||
180 -> GradientDrawable.Orientation.RIGHT_LEFT
|
||||
225 -> GradientDrawable.Orientation.TR_BL
|
||||
270 -> GradientDrawable.Orientation.TOP_BOTTOM
|
||||
315 -> GradientDrawable.Orientation.TL_BR
|
||||
else -> throw IllegalArgumentException("Unsupported angle: $angle")
|
||||
}
|
||||
return orientation
|
||||
}
|
||||
|
||||
fun getColors(): IntArray {
|
||||
return if (useCenterColor && centerColor != null) {
|
||||
intArrayOf(startColor, centerColor!!, endColor)
|
||||
} else intArrayOf(startColor, endColor)
|
||||
}
|
||||
|
||||
fun materialization(): Drawable = DrawableBuilder().batch(this).build()
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
abstract class DrawableWrapperBuilder<T : DrawableWrapperBuilder<T>> {
|
||||
|
||||
protected var drawable: Drawable? = null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun drawable(drawable: Drawable): T = apply { this.drawable = drawable } as T
|
||||
|
||||
abstract fun build(): Drawable
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "CanvasSize")
|
||||
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
class FlipDrawable(
|
||||
private var drawable: Drawable,
|
||||
private var orientation: Int = ORIENTATION_HORIZONTAL
|
||||
) : Drawable() {
|
||||
|
||||
companion object {
|
||||
const val ORIENTATION_HORIZONTAL = 0
|
||||
const val ORIENTATION_VERTICAL = 1
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
val saveCount = canvas.save()
|
||||
if (orientation == ORIENTATION_VERTICAL) {
|
||||
canvas.scale(1f, -1f, (canvas.width / 2).toFloat(), (canvas.height / 2).toFloat())
|
||||
} else {
|
||||
canvas.scale(-1f, 1f, (canvas.width / 2).toFloat(), (canvas.height / 2).toFloat())
|
||||
}
|
||||
drawable.bounds = Rect(0, 0, canvas.width, canvas.height)
|
||||
drawable.draw(canvas)
|
||||
canvas.restoreToCount(saveCount)
|
||||
}
|
||||
|
||||
override fun onLevelChange(level: Int): Boolean {
|
||||
drawable.level = level
|
||||
invalidateSelf()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIntrinsicWidth(): Int {
|
||||
return drawable.intrinsicWidth
|
||||
}
|
||||
|
||||
override fun getIntrinsicHeight(): Int {
|
||||
return drawable.intrinsicHeight
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
drawable.alpha = alpha
|
||||
}
|
||||
|
||||
override fun getOpacity(): Int {
|
||||
return drawable.opacity
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
drawable.colorFilter = colorFilter
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
class FlipDrawableBuilder : DrawableWrapperBuilder<FlipDrawableBuilder>() {
|
||||
|
||||
private var orientation: Int = FlipDrawable.ORIENTATION_HORIZONTAL
|
||||
|
||||
fun orientation(orientation: Int) = apply { this.orientation = orientation }
|
||||
|
||||
override fun build(): Drawable {
|
||||
return FlipDrawable(drawable!!, orientation)
|
||||
}
|
||||
}
|
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
class LayerDrawableBuilder {
|
||||
|
||||
companion object {
|
||||
const val DIMEN_UNDEFINED = Int.MIN_VALUE
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private var paddingMode = LayerDrawable.PADDING_MODE_NEST
|
||||
private var paddingLeft = 0
|
||||
private var paddingTop = 0
|
||||
private var paddingRight = 0
|
||||
private var paddingBottom = 0
|
||||
private var paddingStart = 0
|
||||
private var paddingEnd = 0
|
||||
private val layers = ArrayList<Layer>()
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun paddingMode(mode: Int) = apply { paddingMode = mode }
|
||||
fun paddingLeft(padding: Int) = apply { paddingLeft = padding }
|
||||
fun paddingTop(padding: Int) = apply { paddingTop = padding }
|
||||
fun paddingRight(padding: Int) = apply { paddingRight = padding }
|
||||
fun paddingBottom(padding: Int) = apply { paddingBottom = padding }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun paddingStart(padding: Int) = apply { paddingStart = padding }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun paddingEnd(padding: Int) = apply { paddingEnd = padding }
|
||||
fun padding(padding: Int) = apply {
|
||||
paddingLeft(padding).paddingTop(padding).paddingRight(padding).paddingBottom(padding)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun paddingRelative(padding: Int) = apply {
|
||||
paddingStart(padding).paddingTop(padding).paddingEnd(padding).paddingBottom(padding)
|
||||
}
|
||||
|
||||
fun add(drawable: Drawable) = apply { layers.add(Layer(drawable)) }
|
||||
|
||||
fun width(width: Int) = apply { layers.last().width = width }
|
||||
fun height(height: Int) = apply { layers.last().height = height }
|
||||
|
||||
fun insetLeft(inset: Int) = apply { layers.last().insetLeft = inset }
|
||||
fun insetTop(inset: Int) = apply { layers.last().insetTop = inset }
|
||||
fun insetRight(inset: Int) = apply { layers.last().insetRight = inset }
|
||||
fun insetBottom(inset: Int) = apply { layers.last().insetBottom = inset }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetStart(inset: Int) = apply { layers.last().insetStart = inset }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetEnd(inset: Int) = apply { layers.last().insetEnd = inset }
|
||||
fun inset(inset: Int) =
|
||||
apply { insetLeft(inset).insetTop(inset).insetRight(inset).insetBottom(inset) }
|
||||
|
||||
fun inset(insetLeft: Int, insetTop: Int, insetRight: Int, insetBottom: Int) = apply {
|
||||
insetLeft(insetLeft).insetTop(insetTop).insetRight(insetRight).insetBottom(insetBottom)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetRelative(inset: Int) =
|
||||
apply { insetStart(inset).insetTop(inset).insetEnd(inset).insetBottom(inset) }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun insetRelative(insetStart: Int, insetTop: Int, insetEnd: Int, insetBottom: Int) = apply {
|
||||
insetStart(insetStart).insetTop(insetTop).insetEnd(insetEnd).insetBottom(insetBottom)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun gravity(gravity: Int) = apply { layers.last().gravity = gravity }
|
||||
|
||||
fun modify(
|
||||
index: Int, drawable: Drawable,
|
||||
width: Int = DIMEN_UNDEFINED,
|
||||
height: Int = DIMEN_UNDEFINED,
|
||||
insetLeft: Int = DIMEN_UNDEFINED,
|
||||
insetTop: Int = DIMEN_UNDEFINED,
|
||||
insetRight: Int = DIMEN_UNDEFINED,
|
||||
insetBottom: Int = DIMEN_UNDEFINED,
|
||||
insetStart: Int = DIMEN_UNDEFINED,
|
||||
insetEnd: Int = DIMEN_UNDEFINED
|
||||
) =
|
||||
apply {
|
||||
val layer = layers[index]
|
||||
layer.drawable = drawable
|
||||
if (width != DIMEN_UNDEFINED) layer.width = width
|
||||
if (height != DIMEN_UNDEFINED) layer.height = height
|
||||
if (insetLeft != DIMEN_UNDEFINED) layer.insetLeft = insetLeft
|
||||
if (insetTop != DIMEN_UNDEFINED) layer.insetTop = insetTop
|
||||
if (insetRight != DIMEN_UNDEFINED) layer.insetRight = insetRight
|
||||
if (insetBottom != DIMEN_UNDEFINED) layer.insetBottom = insetBottom
|
||||
if (insetStart != DIMEN_UNDEFINED) layer.insetStart = insetStart
|
||||
if (insetEnd != DIMEN_UNDEFINED) layer.insetEnd = insetEnd
|
||||
}
|
||||
|
||||
fun build(): LayerDrawable {
|
||||
val layerDrawable = LayerDrawable(layers.map { it -> it.drawable }.toTypedArray())
|
||||
for (i in 0 until layers.size) {
|
||||
val layer = layers[i]
|
||||
layerDrawable.setLayerInset(
|
||||
i,
|
||||
layer.insetLeft,
|
||||
layer.insetTop,
|
||||
layer.insetRight,
|
||||
layer.insetBottom
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (layer.insetStart != DIMEN_UNDEFINED || layer.insetEnd != DIMEN_UNDEFINED) {
|
||||
layerDrawable.setLayerInsetRelative(
|
||||
i,
|
||||
layer.insetStart,
|
||||
layer.insetTop,
|
||||
layer.insetEnd,
|
||||
layer.insetBottom
|
||||
)
|
||||
}
|
||||
}
|
||||
layerDrawable.setId(i, i)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
layerDrawable.setLayerGravity(i, layer.gravity)
|
||||
layerDrawable.setLayerInsetStart(i, layer.insetStart)
|
||||
layerDrawable.setLayerInsetEnd(i, layer.insetEnd)
|
||||
}
|
||||
}
|
||||
layerDrawable.paddingMode = paddingMode
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
layerDrawable.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
|
||||
if (paddingStart != DIMEN_UNDEFINED || paddingEnd != DIMEN_UNDEFINED) {
|
||||
layerDrawable.setPaddingRelative(
|
||||
paddingStart,
|
||||
paddingTop,
|
||||
paddingEnd,
|
||||
paddingBottom
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return layerDrawable
|
||||
}
|
||||
|
||||
internal class Layer(var drawable: Drawable) {
|
||||
var gravity: Int = Gravity.NO_GRAVITY
|
||||
var width: Int = -1
|
||||
var height: Int = -1
|
||||
var insetLeft: Int = 0
|
||||
var insetTop: Int = 0
|
||||
var insetRight: Int = 0
|
||||
var insetBottom: Int = 0
|
||||
var insetStart: Int = DIMEN_UNDEFINED
|
||||
var insetEnd: Int = DIMEN_UNDEFINED
|
||||
}
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.Path
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.shapes.PathShape
|
||||
|
||||
class PathShapeDrawableBuilder {
|
||||
|
||||
private var path: Path? = null
|
||||
private var pathStandardWidth: Float = 100f
|
||||
private var pathStandardHeight: Float = 100f
|
||||
private var width: Int = -1
|
||||
private var height: Int = -1
|
||||
|
||||
fun path(path: Path, pathStandardWidth: Float, pathStandardHeight: Float) = apply {
|
||||
this.path = path
|
||||
this.pathStandardWidth = pathStandardWidth
|
||||
this.pathStandardHeight = pathStandardHeight
|
||||
}
|
||||
|
||||
fun width(width: Int) = apply { this.width = width }
|
||||
fun height(height: Int) = apply { this.height = height }
|
||||
fun size(size: Int) = apply { width(size).height(size) }
|
||||
|
||||
fun build(custom: ((shapeDrawable: ShapeDrawable) -> Unit)? = null): ShapeDrawable {
|
||||
val shapeDrawable = ShapeDrawable()
|
||||
if (path == null || width <= 0 || height <= 0) {
|
||||
return shapeDrawable
|
||||
}
|
||||
val pathShape = PathShape(path!!, pathStandardWidth, pathStandardHeight)
|
||||
|
||||
shapeDrawable.shape = pathShape
|
||||
shapeDrawable.intrinsicWidth = width
|
||||
shapeDrawable.intrinsicHeight = height
|
||||
if (custom != null) {
|
||||
custom(shapeDrawable)
|
||||
}
|
||||
return shapeDrawable
|
||||
}
|
||||
}
|
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.*
|
||||
import android.util.StateSet
|
||||
|
||||
class RippleDrawableBuilder : DrawableWrapperBuilder<RippleDrawableBuilder>() {
|
||||
|
||||
private var color: Int = Constants.DEFAULT_COLOR
|
||||
private var colorStateList: ColorStateList? = null
|
||||
private var radius: Int = -1
|
||||
|
||||
fun color(color: Int) = apply { this.color = color }
|
||||
fun colorStateList(colorStateList: ColorStateList?) =
|
||||
apply { this.colorStateList = colorStateList }
|
||||
|
||||
fun radius(radius: Int) = apply { this.radius = radius }
|
||||
|
||||
override fun build(): Drawable {
|
||||
var drawable = this.drawable!!
|
||||
val colorStateList = this.colorStateList ?: ColorStateList(
|
||||
arrayOf(StateSet.WILD_CARD),
|
||||
intArrayOf(color)
|
||||
)
|
||||
|
||||
var mask = if (drawable is DrawableContainer) drawable.getCurrent() else drawable
|
||||
if (mask is ShapeDrawable) {
|
||||
val state = mask.getConstantState()
|
||||
if (state != null) {
|
||||
val temp = state.newDrawable().mutate() as ShapeDrawable
|
||||
temp.paint.color = Color.BLACK
|
||||
mask = temp
|
||||
}
|
||||
} else if (mask is GradientDrawable) {
|
||||
val state = mask.getConstantState()
|
||||
if (state != null) {
|
||||
val temp = state.newDrawable().mutate() as GradientDrawable
|
||||
temp.setColor(Color.BLACK)
|
||||
mask = temp
|
||||
}
|
||||
} else {
|
||||
mask = ColorDrawable(Color.BLACK)
|
||||
}
|
||||
|
||||
val rippleDrawable = RippleDrawable(colorStateList, drawable, mask)
|
||||
setRadius(rippleDrawable, radius)
|
||||
rippleDrawable.invalidateSelf()
|
||||
drawable = rippleDrawable
|
||||
return drawable
|
||||
}
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.RotateDrawable
|
||||
|
||||
class RotateDrawableBuilder : DrawableWrapperBuilder<RotateDrawableBuilder>() {
|
||||
|
||||
private var pivotX: Float = 0.5f
|
||||
private var pivotY: Float = 0.5f
|
||||
private var fromDegrees: Float = 0f
|
||||
private var toDegrees: Float = 360f
|
||||
|
||||
fun pivotX(x: Float) = apply { pivotX = x }
|
||||
fun pivotY(y: Float) = apply { pivotY = y }
|
||||
fun fromDegrees(degree: Float) = apply { fromDegrees = degree }
|
||||
fun toDegrees(degree: Float) = apply { toDegrees = degree }
|
||||
|
||||
override fun build(): Drawable {
|
||||
val rotateDrawable = RotateDrawable()
|
||||
drawable?.let {
|
||||
setDrawable(rotateDrawable, it)
|
||||
apply {
|
||||
setPivotX(rotateDrawable, pivotX)
|
||||
setPivotY(rotateDrawable, pivotY)
|
||||
setFromDegrees(rotateDrawable, fromDegrees)
|
||||
setToDegrees(rotateDrawable, toDegrees)
|
||||
}
|
||||
}
|
||||
return rotateDrawable
|
||||
}
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.ScaleDrawable
|
||||
import android.view.Gravity
|
||||
|
||||
class ScaleDrawableBuilder : DrawableWrapperBuilder<ScaleDrawableBuilder>() {
|
||||
|
||||
private var level: Int = 10000
|
||||
private var scaleGravity = Gravity.CENTER
|
||||
private var scaleWidth: Float = 0f
|
||||
private var scaleHeight: Float = 0f
|
||||
|
||||
fun level(level: Int) = apply { this.level = level }
|
||||
fun scaleGravity(gravity: Int) = apply { this.scaleGravity = gravity }
|
||||
fun scaleWidth(scale: Float) = apply { this.scaleWidth = scale }
|
||||
fun scaleHeight(scale: Float) = apply { this.scaleHeight = scale }
|
||||
|
||||
override fun build(): Drawable {
|
||||
val scaleDrawable = ScaleDrawable(drawable, scaleGravity, scaleWidth, scaleHeight)
|
||||
scaleDrawable.level = level
|
||||
return scaleDrawable
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.StateListDrawable
|
||||
import android.util.StateSet
|
||||
|
||||
class StateListDrawableBuilder {
|
||||
|
||||
private var pressed: Drawable? = null
|
||||
private var disabled: Drawable? = null
|
||||
private var selected: Drawable? = null
|
||||
private var normal: Drawable = ColorDrawable(Color.TRANSPARENT)
|
||||
|
||||
fun pressed(pressed: Drawable?) = apply { this.pressed = pressed }
|
||||
fun disabled(disabled: Drawable?) = apply { this.disabled = disabled }
|
||||
fun selected(selected: Drawable?) = apply { this.selected = selected }
|
||||
fun normal(normal: Drawable) = apply { this.normal = normal }
|
||||
|
||||
fun build(): StateListDrawable {
|
||||
val stateListDrawable = StateListDrawable()
|
||||
setupStateListDrawable(stateListDrawable)
|
||||
return stateListDrawable
|
||||
}
|
||||
|
||||
private fun setupStateListDrawable(stateListDrawable: StateListDrawable) {
|
||||
pressed?.let {
|
||||
stateListDrawable.addState(intArrayOf(android.R.attr.state_pressed), it)
|
||||
}
|
||||
disabled?.let {
|
||||
stateListDrawable.addState(intArrayOf(-android.R.attr.state_enabled), it)
|
||||
}
|
||||
selected?.let {
|
||||
stateListDrawable.addState(intArrayOf(android.R.attr.state_selected), it)
|
||||
}
|
||||
stateListDrawable.addState(StateSet.WILD_CARD, normal)
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/10/6.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.factory
|
||||
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
/** 已添加的返回监听事件 */
|
||||
private val onBackPressedCallbacks = HashMap<AppCompatActivity, OnBackPressedCallback>()
|
||||
|
||||
/**
|
||||
* 手动调用返回事件
|
||||
* @param ignored 是否忽略现有返回监听事件立即返回 - 否则将执行返回事件 - 默认否
|
||||
*/
|
||||
fun AppCompatActivity.callOnBackPressed(ignored: Boolean = false) {
|
||||
if (isDestroyed) return
|
||||
onBackPressedCallbacks[this]?.isEnabled = ignored.not()
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加返回监听事件
|
||||
* @param callback 回调事件
|
||||
*/
|
||||
fun AppCompatActivity.addOnBackPressedEvent(callback: OnBackPressedEvent.() -> Unit) {
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
OnBackPressedEvent(this@addOnBackPressedEvent).apply(callback)
|
||||
}
|
||||
}.also { result ->
|
||||
onBackPressedCallbacks.computeIfAbsent(this) {
|
||||
onBackPressedDispatcher.addCallback(result)
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回监听事件实现类
|
||||
* @param instance 当前实例
|
||||
*/
|
||||
class OnBackPressedEvent(private val instance: AppCompatActivity) {
|
||||
|
||||
/** 立即释放返回事件并调用返回功能 */
|
||||
fun releaseEventAndBack() = instance.callOnBackPressed(ignored = true)
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/6/3.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.fankes.coloros.notify.utils.factory
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.ListView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
|
||||
/**
|
||||
* 绑定 [BaseAdapter] 到 [ListView]
|
||||
* @param initiate 方法体
|
||||
* @return [BaseAdapter]
|
||||
*/
|
||||
inline fun ListView.bindAdapter(initiate: BaseAdapterCreater.() -> Unit) =
|
||||
BaseAdapterCreater(context).apply(initiate).baseAdapter?.apply { adapter = this } ?: error("BaseAdapter not binded")
|
||||
|
||||
/**
|
||||
* [BaseAdapter] 创建类
|
||||
* @param context 实例
|
||||
*/
|
||||
class BaseAdapterCreater(val context: Context) {
|
||||
|
||||
/** 当前 [List] 回调 */
|
||||
var listDataCallback: (() -> List<*>)? = null
|
||||
|
||||
/** 当前 [BaseAdapter] */
|
||||
var baseAdapter: BaseAdapter? = null
|
||||
|
||||
/**
|
||||
* 绑定 [List] 到 [ListView]
|
||||
* @param result 回调数据
|
||||
*/
|
||||
fun onBindDatas(result: (() -> List<*>)) {
|
||||
listDataCallback = result
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定 [BaseAdapter] 到 [ListView]
|
||||
* @param bindViews 回调 - ([VB] 每项,[Int] 下标)
|
||||
*/
|
||||
inline fun <reified VB : ViewBinding> onBindViews(crossinline bindViews: (binding: VB, position: Int) -> Unit) {
|
||||
baseAdapter = object : BaseAdapter() {
|
||||
override fun getCount() = listDataCallback?.let { it() }?.size ?: 0
|
||||
override fun getItem(position: Int) = listDataCallback?.let { it() }?.get(position)
|
||||
override fun getItemId(position: Int) = position.toLong()
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||
var holderView = convertView
|
||||
val holder: VB
|
||||
if (convertView == null) {
|
||||
holder = VB::class.java.method {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}.get().invoke<VB>(LayoutInflater.from(context)) ?: error("ViewHolder binding failed")
|
||||
holderView = holder.root.apply { tag = holder }
|
||||
} else holder = convertView.tag as VB
|
||||
bindViews(holder, position)
|
||||
return holderView ?: error("ViewHolder binding failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,102 +18,105 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/7.
|
||||
* This file is created by fankes on 2022/1/7.
|
||||
*/
|
||||
@file:Suppress("unused", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE")
|
||||
@file:Suppress("unused", "DEPRECATION")
|
||||
|
||||
package com.fankes.coloros.notify.utils.factory
|
||||
|
||||
import android.app.Dialog
|
||||
import android.app.TimePickerDialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.highcapable.yukihookapi.annotation.DoNotUseField
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
|
||||
/**
|
||||
* 构造对话框
|
||||
* @param isUseBlackTheme 是否使用深色主题
|
||||
* @param it 对话框方法体
|
||||
*/
|
||||
fun Context.showDialog(isUseBlackTheme: Boolean = false, it: DialogBuilder.() -> Unit) =
|
||||
DialogBuilder(this, isUseBlackTheme).apply(it).show()
|
||||
|
||||
/**
|
||||
* 显示时间选择对话框
|
||||
* @param timeSet 当前时间 - 不写将使用当前时间格式:HH:mm
|
||||
* @param it 回调 - 小时与分钟 HH:mm
|
||||
* @param result 回调 - 小时与分钟 HH:mm
|
||||
*/
|
||||
fun Context.showTimePicker(timeSet: String = "", it: (String) -> Unit) =
|
||||
TimePickerDialog(this, { _, h, m -> it("${h.autoZero}:${m.autoZero}") }, timeSet.hour, timeSet.minute, true).show()
|
||||
fun Context.showTimePicker(timeSet: String = "", result: (String) -> Unit) =
|
||||
TimePickerDialog(this, { _, h, m -> result("${h.autoZero}:${m.autoZero}") }, timeSet.hour, timeSet.minute, true).show()
|
||||
|
||||
/**
|
||||
* 构造 [VB] 自定义 View 对话框
|
||||
* @param initiate 对话框方法体
|
||||
*/
|
||||
@JvmName(name = "showDialog_Generics")
|
||||
inline fun <reified VB : ViewBinding> Context.showDialog(initiate: DialogBuilder<VB>.() -> Unit) =
|
||||
DialogBuilder<VB>(context = this, VB::class.java).apply(initiate).show()
|
||||
|
||||
/**
|
||||
* 构造对话框
|
||||
* @param initiate 对话框方法体
|
||||
*/
|
||||
inline fun Context.showDialog(initiate: DialogBuilder<*>.() -> Unit) = DialogBuilder<ViewBinding>(context = this).apply(initiate).show()
|
||||
|
||||
/**
|
||||
* 对话框构造器
|
||||
* @param context 实例
|
||||
* @param isUseBlackTheme 是否使用深色主题 - 对 AndroidX 风格无效
|
||||
* @param bindingClass [ViewBinding] 的 [Class] 实例 or null
|
||||
*/
|
||||
class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean) {
|
||||
class DialogBuilder<VB : ViewBinding>(val context: Context, private val bindingClass: Class<*>? = null) {
|
||||
|
||||
private var instanceAndroidX: androidx.appcompat.app.AlertDialog.Builder? = null // 实例对象
|
||||
private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象
|
||||
/** 实例对象 */
|
||||
private var instance: AlertDialog.Builder? = null
|
||||
|
||||
private var dialogInstance: Dialog? = null // 对话框实例
|
||||
/** 对话框取消监听 */
|
||||
private var onCancel: (() -> Unit)? = null
|
||||
|
||||
@DoNotUseField
|
||||
var customLayoutView: View? = null // 自定义布局
|
||||
/** 对话框实例 */
|
||||
private var dialogInstance: Dialog? = null
|
||||
|
||||
/** 自定义布局 */
|
||||
private var customLayoutView: View? = null
|
||||
|
||||
/**
|
||||
* 是否需要使用 AndroidX 风格对话框
|
||||
* @return [Boolean]
|
||||
* 获取 [DialogBuilder] 绑定布局对象
|
||||
* @return [VB]
|
||||
*/
|
||||
private val isUsingAndroidX get() = runCatching { context is AppCompatActivity }.getOrNull() ?: false
|
||||
val binding by lazy {
|
||||
bindingClass?.method {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}?.get()?.invoke<VB>(LayoutInflater.from(context))?.apply {
|
||||
customLayoutView = root
|
||||
} ?: error("This dialog maybe not a custom view dialog")
|
||||
}
|
||||
|
||||
init {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX = MaterialAlertDialogBuilder(context) }
|
||||
else runInSafe {
|
||||
instanceAndroid = android.app.AlertDialog.Builder(
|
||||
context,
|
||||
if (isUseBlackTheme) android.R.style.Theme_Material_Dialog else android.R.style.Theme_Material_Light_Dialog
|
||||
)
|
||||
}
|
||||
if (YukiHookAPI.Status.isXposedEnvironment) error("This dialog is not allowed to created in Xposed environment")
|
||||
instance = MaterialAlertDialogBuilder(context)
|
||||
}
|
||||
|
||||
/** 设置对话框不可关闭 */
|
||||
fun noCancelable() {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setCancelable(false) }
|
||||
else runInSafe { instanceAndroid?.setCancelable(false) }
|
||||
instance?.setCancelable(false)
|
||||
}
|
||||
|
||||
/** 设置对话框标题 */
|
||||
var title
|
||||
get() = ""
|
||||
set(value) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setTitle(value) }
|
||||
else runInSafe { instanceAndroid?.setTitle(value) }
|
||||
instance?.setTitle(value)
|
||||
}
|
||||
|
||||
/** 设置对话框消息内容 */
|
||||
var msg
|
||||
get() = ""
|
||||
set(value) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setMessage(value) }
|
||||
else runInSafe { instanceAndroid?.setMessage(value) }
|
||||
instance?.setMessage(value)
|
||||
}
|
||||
|
||||
/** 设置进度条对话框消息内容 */
|
||||
@@ -124,7 +127,10 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
|
||||
customLayoutView = LinearLayout(context).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER or Gravity.START
|
||||
addView(ProgressBar(context))
|
||||
addView(CircularProgressIndicator(context).apply {
|
||||
isIndeterminate = true
|
||||
trackCornerRadius = 10.dp(context)
|
||||
})
|
||||
addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
|
||||
addView(TextView(context).apply {
|
||||
tag = "progressContent"
|
||||
@@ -135,75 +141,52 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
|
||||
else customLayoutView?.findViewWithTag<TextView>("progressContent")?.text = value
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框自定义布局
|
||||
* @return [ViewBinding]
|
||||
*/
|
||||
inline fun <reified T : ViewBinding> bind() =
|
||||
T::class.java.method {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}.get().invoke<T>(LayoutInflater.from(context))?.apply {
|
||||
customLayoutView = root
|
||||
} ?: error("binding failed")
|
||||
|
||||
/**
|
||||
* 设置对话框确定按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun confirmButton(text: String = "确定", it: () -> Unit = {}) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setPositiveButton(text) { _, _ -> it() } }
|
||||
else runInSafe { instanceAndroid?.setPositiveButton(text) { _, _ -> it() } }
|
||||
fun confirmButton(text: String = "确定", callback: () -> Unit = {}) {
|
||||
instance?.setPositiveButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框取消按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun cancelButton(text: String = "取消", it: () -> Unit = {}) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setNegativeButton(text) { _, _ -> it() } }
|
||||
else runInSafe { instanceAndroid?.setNegativeButton(text) { _, _ -> it() } }
|
||||
fun cancelButton(text: String = "取消", callback: () -> Unit = {}) {
|
||||
instance?.setNegativeButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框第三个按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun neutralButton(text: String = "更多", it: () -> Unit = {}) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setNeutralButton(text) { _, _ -> it() } }
|
||||
else runInSafe { instanceAndroid?.setNeutralButton(text) { _, _ -> it() } }
|
||||
fun neutralButton(text: String = "更多", callback: () -> Unit = {}) {
|
||||
instance?.setNeutralButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 当对话框关闭时
|
||||
* @param callback 回调
|
||||
*/
|
||||
fun onCancel(callback: () -> Unit) {
|
||||
onCancel = callback
|
||||
}
|
||||
|
||||
/** 取消对话框 */
|
||||
fun cancel() = dialogInstance?.cancel()
|
||||
|
||||
/** 显示对话框 */
|
||||
internal fun show() =
|
||||
if (isUsingAndroidX) runInSafe {
|
||||
instanceAndroidX?.create()?.apply {
|
||||
customLayoutView?.let { setView(it) }
|
||||
dialogInstance = this
|
||||
}?.show()
|
||||
} else runInSafe {
|
||||
instanceAndroid?.create()?.apply {
|
||||
customLayoutView?.let { setView(it) }
|
||||
window?.setBackgroundDrawable(
|
||||
GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
if (isUseBlackTheme) intArrayOf(0xFF2D2D2D.toInt(), 0xFF2D2D2D.toInt())
|
||||
else intArrayOf(Color.WHITE, Color.WHITE)
|
||||
).apply {
|
||||
shape = GradientDrawable.RECTANGLE
|
||||
gradientType = GradientDrawable.LINEAR_GRADIENT
|
||||
cornerRadius = 15.dpFloat(this@DialogBuilder.context)
|
||||
})
|
||||
dialogInstance = this
|
||||
}?.show()
|
||||
}
|
||||
fun show() = runInSafe {
|
||||
/** 若当前自定义 View 的对话框没有调用 [binding] 将会对其手动调用一次以确保显示布局 */
|
||||
if (bindingClass != null) binding
|
||||
instance?.create()?.apply {
|
||||
customLayoutView?.let { setView(it) }
|
||||
dialogInstance = this
|
||||
setOnCancelListener { onCancel?.invoke() }
|
||||
}?.show()
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,13 +18,13 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/13.
|
||||
* This file is created by fankes on 2022/3/13.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.coloros.notify.utils.factory
|
||||
|
||||
import com.highcapable.yukihookapi.hook.log.loggerE
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
@@ -79,5 +79,5 @@ inline fun <T> safeOf(default: T, result: () -> T) = try {
|
||||
* @param block 正常回调
|
||||
*/
|
||||
inline fun <T> T.runInSafe(msg: String = "", block: () -> Unit) {
|
||||
runCatching(block).onFailure { if (msg.isNotBlank()) loggerE(msg = msg, e = it) }
|
||||
runCatching(block).onFailure { if (msg.isNotBlank()) YLog.error(msg, it) }
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,9 +18,9 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/7.
|
||||
* This file is created by fankes on 2022/1/7.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt")
|
||||
@file:Suppress("unused", "ObsoleteSdkInt", "DEPRECATION")
|
||||
|
||||
package com.fankes.coloros.notify.utils.factory
|
||||
|
||||
@@ -28,14 +28,17 @@ import android.app.Activity
|
||||
import android.app.Notification
|
||||
import android.app.Service
|
||||
import android.app.WallpaperManager
|
||||
import android.app.WallpaperManager.FLAG_SYSTEM
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.PackageInfoFlags
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
@@ -47,20 +50,28 @@ import android.os.Handler
|
||||
import android.provider.Settings
|
||||
import android.util.Base64
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.net.toUri
|
||||
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.factory.field
|
||||
import com.highcapable.yukihookapi.hook.factory.hasClass
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.java.StringType
|
||||
import com.highcapable.yukihookapi.hook.factory.toClassOrNull
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.type.java.StringClass
|
||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 系统深色模式是否开启
|
||||
@@ -86,6 +97,12 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
|
||||
*/
|
||||
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
|
||||
|
||||
/**
|
||||
* 系统版本是否高于或等于 Android 13
|
||||
* @return [Boolean] 是否符合条件
|
||||
*/
|
||||
inline val isUpperOfAndroidT get() = Build.VERSION.SDK_INT > Build.VERSION_CODES.S
|
||||
|
||||
/**
|
||||
* 系统版本是否高于或等于 Android 12
|
||||
* @return [Boolean] 是否符合条件
|
||||
@@ -102,7 +119,7 @@ inline val isLowerAndroidP get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
|
||||
* 当前设备是否是 ColorOS 定制 Android 系统
|
||||
* @return [Boolean] 是否符合条件
|
||||
*/
|
||||
val isColorOS by lazy { ("oppo.R").hasClass || ("com.color.os.ColorBuild").hasClass || ("oplus.R").hasClass }
|
||||
val isColorOS by lazy { "oppo.R".hasClass() || "com.color.os.ColorBuild".hasClass() || "oplus.R".hasClass() }
|
||||
|
||||
/**
|
||||
* 当前设备是否不是 ColorOS 定制 Android 系统
|
||||
@@ -117,7 +134,7 @@ inline val isNotColorOS get() = !isColorOS
|
||||
val isRealmeUI
|
||||
get() = safeOfFalse {
|
||||
val query = appContext.contentResolver.query(
|
||||
Uri.parse("content://com.oplus.customize.coreapp.configmanager.configprovider.AppFeatureProvider")
|
||||
"content://com.oplus.customize.coreapp.configmanager.configprovider.AppFeatureProvider".toUri()
|
||||
.buildUpon()
|
||||
.appendPath("app_feature")
|
||||
.build(), null, "featurename=?", arrayOf("com.android.launcher.device_rm"), null
|
||||
@@ -127,6 +144,29 @@ val isRealmeUI
|
||||
isExist
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Android 版本代号
|
||||
* @return [String]
|
||||
*/
|
||||
val androidVersionCodeName
|
||||
get() = when (Build.VERSION.SDK_INT) {
|
||||
34 -> "U"
|
||||
33 -> "T"
|
||||
32 -> "S_V2"
|
||||
31 -> "S"
|
||||
30 -> "R"
|
||||
29 -> "Q"
|
||||
28 -> "P"
|
||||
27 -> "O_MR1"
|
||||
26 -> "O"
|
||||
25 -> "N_MR1"
|
||||
24 -> "N"
|
||||
23 -> "M"
|
||||
22 -> "L_MR1"
|
||||
21 -> "L"
|
||||
else -> "?"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 ColorOS 完整版本
|
||||
* @return [String]
|
||||
@@ -137,61 +177,102 @@ val colorOSFullVersion get() = "${if (isRealmeUI) "RealmeUI " else ""}$colorOSVe
|
||||
* 获取 ColorOS 版本
|
||||
* @return [String]
|
||||
*/
|
||||
val colorOSVersion
|
||||
get() = safeOf(default = "无法获取") {
|
||||
(classOf(name = "com.oplus.os.OplusBuild").let {
|
||||
it.field { name = "VERSIONS" }.ignoredError().get().array<String>().takeIf { e -> e.isNotEmpty() }
|
||||
?.get(it.method { name = "getOplusOSVERSION" }.ignoredError().get().int() - 1)
|
||||
} ?: findPropString(
|
||||
key = "ro.system.build.fingerprint", default = "无法获取"
|
||||
).split("ssi:")[1].split("/")[0].trim()) + " ${Build.DISPLAY}"
|
||||
}
|
||||
val colorOSVersion get() = "$colorOSNumberVersion ${Build.DISPLAY}"
|
||||
|
||||
/**
|
||||
* 得到安装包信息
|
||||
* @return [PackageInfo]
|
||||
*/
|
||||
val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?: PackageInfo()
|
||||
|
||||
/**
|
||||
* 判断应用是否安装
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val String.isInstall
|
||||
get() = safeOfFalse {
|
||||
appContext.packageManager.getPackageInfo(
|
||||
this, PackageManager.GET_UNINSTALLED_PACKAGES
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到版本信息
|
||||
* 获取 ColorOS 数字版本
|
||||
* @return [String]
|
||||
*/
|
||||
val Context.versionName get() = packageInfo.versionName ?: ""
|
||||
val colorOSNumberVersion
|
||||
get() = safeOf(default = "无法获取") {
|
||||
"com.oplus.os.OplusBuild".toClassOrNull()?.let {
|
||||
it.field { name = "VERSIONS" }.ignored().get().array<String>().takeIf { e -> e.isNotEmpty() }
|
||||
?.get(it.method { name = "getOplusOSVERSION" }.ignored().get().int() - 1)
|
||||
} ?: findPropString("ro.system.build.fingerprint", "无法获取").split("ssi:")[1].split("/")[0].trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到版本号
|
||||
* 获取 [Drawable]
|
||||
* @param resId 属性资源 ID
|
||||
* @return [Drawable]
|
||||
*/
|
||||
fun Resources.drawableOf(@DrawableRes resId: Int) = ResourcesCompat.getDrawable(this, resId, null) ?: error("Invalid resources")
|
||||
|
||||
/**
|
||||
* 获取颜色
|
||||
* @param resId 属性资源 ID
|
||||
* @return [Int]
|
||||
*/
|
||||
val Context.versionCode get() = packageInfo.versionCode
|
||||
fun Resources.colorOf(@ColorRes resId: Int) = ResourcesCompat.getColor(this, resId, null)
|
||||
|
||||
/**
|
||||
* 得到 APP 安装包信息 (兼容)
|
||||
* @param packageName APP 包名
|
||||
* @param flag [PackageInfoFlags]
|
||||
* @return [PackageInfo] or null
|
||||
*/
|
||||
private fun Context.getPackageInfoCompat(packageName: String, flag: Number = 0) = runCatching {
|
||||
@Suppress("DEPRECATION", "KotlinRedundantDiagnosticSuppress")
|
||||
if (Build.VERSION.SDK_INT >= 33)
|
||||
packageManager?.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong()))
|
||||
else packageManager?.getPackageInfo(packageName, flag.toInt())
|
||||
}.getOrNull()
|
||||
|
||||
/**
|
||||
* 得到 APP 版本号 (兼容 [PackageInfo.getLongVersionCode])
|
||||
* @return [Int]
|
||||
*/
|
||||
private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this)
|
||||
|
||||
/**
|
||||
* 判断 APP 是否安装
|
||||
* @param packageName APP 包名
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun Context.isInstall(packageName: String) = getPackageInfoCompat(packageName)?.let { true } ?: false
|
||||
|
||||
/**
|
||||
* 得到 APP 版本信息
|
||||
* @return [String]
|
||||
*/
|
||||
val Context.appVersionName get() = getPackageInfoCompat(packageName)?.versionName ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本号
|
||||
* @return [Int]
|
||||
*/
|
||||
val Context.appVersionCode get() = getPackageInfoCompat(packageName)?.versionCodeCompat
|
||||
|
||||
/**
|
||||
* 得到 APP 名称
|
||||
* @param name APP 包名
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String]
|
||||
*/
|
||||
fun Context.findAppName(name: String) =
|
||||
safeOfNothing { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadLabel(packageManager).toString() }
|
||||
fun Context.appNameOf(packageName: String = getPackageName()) =
|
||||
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 图标
|
||||
* @param name APP 包名
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [Drawable] or null
|
||||
*/
|
||||
fun Context.findAppIcon(name: String) =
|
||||
safeOfNull { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadIcon(packageManager) }
|
||||
fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
|
||||
|
||||
/**
|
||||
* 获取 APP 是否为 DEBUG 版本
|
||||
* @param packageName APP 包名
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun Context.isDebugApp(packageName: String) =
|
||||
safeOfFalse { (getPackageInfoCompat(packageName)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 }
|
||||
|
||||
/**
|
||||
* 获取 APP 是否为系统 APP
|
||||
* @param packageName APP 包名
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun Context.isSystemApp(packageName: String) =
|
||||
safeOfFalse { (getPackageInfoCompat(packageName)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) ?: 0) != 0 }
|
||||
|
||||
/**
|
||||
* 对数值自动补零
|
||||
@@ -231,8 +312,11 @@ val isNotNoificationEnabled get() = !NotificationManagerCompat.from(appContext).
|
||||
* 网络连接是否正常
|
||||
* @return [Boolean] 网络是否连接
|
||||
*/
|
||||
val isNetWorkSuccess
|
||||
get() = safeOfFalse { appContext.getSystemService<ConnectivityManager>()?.activeNetworkInfo != null }
|
||||
val Context.isNetWorkSuccess
|
||||
get() = safeOfFalse {
|
||||
@Suppress("DEPRECATION")
|
||||
getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
|
||||
}
|
||||
|
||||
/**
|
||||
* dp 转换为 pxInt
|
||||
@@ -249,21 +333,32 @@ fun Number.dp(context: Context) = dpFloat(context).toInt()
|
||||
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
|
||||
|
||||
/**
|
||||
* 获取系统壁纸颜色
|
||||
* 获取系统主题色
|
||||
*
|
||||
* 由于 ColorOS 阉割了 [android.R.color.system_accent1_600] 这里取系统壁纸颜色做补偿
|
||||
* @return [Int] 无法获取时返回透明色
|
||||
*
|
||||
* 从 ColorOS 13 (Android 13) 开始使用系统取色方案
|
||||
* @return [Int] Android < 13 返回 [wallpaperColor]
|
||||
*/
|
||||
val Context.systemAccentColor
|
||||
get() = safeOf(wallpaperColor) {
|
||||
if (isUpperOfAndroidT) resources.colorOf(android.R.color.system_accent1_600) else wallpaperColor
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统壁纸颜色
|
||||
* @return [Int] 无法获取时返回默认颜色
|
||||
*/
|
||||
val Context.wallpaperColor
|
||||
get() = safeOfNan {
|
||||
WallpaperManager.getInstance(this).getWallpaperColors(FLAG_SYSTEM)?.secondaryColor?.toArgb() ?: 0
|
||||
}
|
||||
get() = runCatching {
|
||||
WallpaperManager.getInstance(this).getWallpaperColors(WallpaperManager.FLAG_SYSTEM)?.primaryColor?.toArgb()
|
||||
}.getOrNull() ?: (if (isSystemInDarkMode) 0xFFD8D8D8.toInt() else 0xFF707173.toInt())
|
||||
|
||||
/**
|
||||
* 是否为白色
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val Int.isWhite
|
||||
val Int.isWhiteColor
|
||||
get() = safeOfTrue {
|
||||
val r = this and 0xff0000 shr 16
|
||||
val g = this and 0x00ff00 shr 8
|
||||
@@ -271,6 +366,13 @@ val Int.isWhite
|
||||
(0.2126 * r + 0.7152 * g + 0.0722 * b) >= 128
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整颜色透明度
|
||||
* @param value 透明度
|
||||
* @return [Int] 调整后的颜色
|
||||
*/
|
||||
fun Int.colorAlphaOf(value: Float) = safeOfNan { (255.coerceAtMost(0.coerceAtLeast((value * 255).toInt())) shl 24) + (0x00ffffff and this) }
|
||||
|
||||
/**
|
||||
* Base64 加密
|
||||
* @return [String]
|
||||
@@ -313,19 +415,30 @@ val String.bitmap: Bitmap get() = unbase64.bitmap
|
||||
* @return [String]
|
||||
*/
|
||||
fun findPropString(key: String, default: String = "") = safeOf(default) {
|
||||
(classOf(name = "android.os.SystemProperties").method {
|
||||
"android.os.SystemProperties".toClassOrNull()?.method {
|
||||
name = "get"
|
||||
param(StringType, StringType)
|
||||
}.get().invoke(key, default)) ?: default
|
||||
param(StringClass, StringClass)
|
||||
}?.get()?.invoke(key, default) ?: default
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行命令 - su
|
||||
* 是否有 Root 权限
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val isRootAccess get() = safeOfFalse {
|
||||
@Suppress("DEPRECATION")
|
||||
Shell.rootAccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行命令
|
||||
* @param cmd 命令
|
||||
* @param isSu 是否使用 Root 权限执行 - 默认:是
|
||||
* @return [String] 执行结果
|
||||
*/
|
||||
fun execShellSu(cmd: String) = safeOfNothing {
|
||||
Shell.su(cmd).exec().out.let {
|
||||
fun execShell(cmd: String, isSu: Boolean = true) = safeOfNothing {
|
||||
@Suppress("DEPRECATION")
|
||||
(if (isSu) Shell.su(cmd) else Shell.sh(cmd)).exec().out.let {
|
||||
if (it.isNotEmpty()) it[0].trim() else ""
|
||||
}
|
||||
}
|
||||
@@ -334,34 +447,34 @@ fun execShellSu(cmd: String) = safeOfNothing {
|
||||
* 弹出 [Toast]
|
||||
* @param msg 提示内容
|
||||
*/
|
||||
fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).show()
|
||||
fun toast(msg: String) {
|
||||
runCatching {
|
||||
Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).show()
|
||||
}.onFailure { YLog.warn(msg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到指定页面
|
||||
*
|
||||
* [T] 为指定的 [Activity]
|
||||
*/
|
||||
inline fun <reified T : Activity> Context.navigate() = runInSafe { startActivity(Intent(this, T::class.java)) }
|
||||
|
||||
/**
|
||||
* 跳转到指定页面
|
||||
*
|
||||
* [T] 为指定的 [Activity]
|
||||
*/
|
||||
inline fun <reified T : Activity> Service.navigate() =
|
||||
runInSafe { startActivity(Intent(this, T::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }) }
|
||||
inline fun <reified T : Activity> Context.navigate() = runInSafe {
|
||||
startActivity(Intent(if (this is Service) applicationContext else this, T::class.java).apply {
|
||||
if (this@navigate !is Activity) flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出 [Snackbar]
|
||||
* @param msg 提示内容
|
||||
* @param actionText 按钮文本 - 不写默认取消按钮
|
||||
* @param it 按钮事件回调
|
||||
* @param callback 按钮事件回调
|
||||
*/
|
||||
fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) =
|
||||
fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {}) =
|
||||
Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).apply {
|
||||
if (actionText.isBlank()) return@apply
|
||||
setActionTextColor(if (isSystemInDarkMode) Color.BLACK else Color.WHITE)
|
||||
setAction(actionText) { it() }
|
||||
setAction(actionText) { callback() }
|
||||
}.show()
|
||||
|
||||
/**
|
||||
@@ -373,7 +486,7 @@ fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
|
||||
startActivity(Intent().apply {
|
||||
if (packageName.isNotBlank()) setPackage(packageName)
|
||||
action = Intent.ACTION_VIEW
|
||||
data = Uri.parse(url)
|
||||
data = url.toUri()
|
||||
/** 防止顶栈一样重叠在自己的 APP 中 */
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
})
|
||||
@@ -387,7 +500,7 @@ fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
|
||||
* @param packageName 包名
|
||||
*/
|
||||
fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching {
|
||||
if (packageName.isInstall)
|
||||
if (isInstall(packageName))
|
||||
startActivity(Intent().apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
@@ -432,4 +545,30 @@ fun Long.stampToDate(format: String = "yyyy-MM-dd HH:mm:ss") =
|
||||
* @param ms 毫秒 - 默认:150
|
||||
* @param it 方法体
|
||||
*/
|
||||
fun Any?.delayedRun(ms: Long = 150, it: () -> Unit) = runInSafe { Handler().postDelayed({ it() }, ms) }
|
||||
fun Any?.delayedRun(ms: Long = 150, it: () -> Unit) = runInSafe {
|
||||
@Suppress("DEPRECATION")
|
||||
Handler().postDelayed({ it() }, ms)
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏或显示启动器图标
|
||||
*
|
||||
* - 你可能需要 LSPosed 的最新版本以开启高版本系统中隐藏 APP 桌面图标功能
|
||||
* @param isShow 是否显示
|
||||
*/
|
||||
fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
|
||||
packageManager?.setComponentEnabledSetting(
|
||||
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home"),
|
||||
if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.DONT_KILL_APP
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取启动器图标状态
|
||||
* @return [Boolean] 是否显示
|
||||
*/
|
||||
val Context.isLauncherIconShowing
|
||||
get() = packageManager?.getComponentEnabledSetting(
|
||||
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home")
|
||||
) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/4/17.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.fankes.coloros.notify.R
|
||||
import com.fankes.coloros.notify.utils.factory.appIconOf
|
||||
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
|
||||
|
||||
/**
|
||||
* 模块更新激活提醒通知工具类
|
||||
*/
|
||||
object ActivationPromptTool {
|
||||
|
||||
/** 当前模块的包名 */
|
||||
private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID
|
||||
|
||||
/** 推送通知的渠道名称 */
|
||||
private const val NOTIFY_CHANNEL = "activationPromptId"
|
||||
|
||||
/**
|
||||
* 推送提醒通知
|
||||
* @param context 当前实例
|
||||
* @param packageName 当前 APP 包名
|
||||
*/
|
||||
fun prompt(context: Context, packageName: String) {
|
||||
if (packageName != BuildConfigWrapper.APPLICATION_ID) return
|
||||
context.getSystemService(NotificationManager::class.java)?.apply {
|
||||
createNotificationChannel(
|
||||
NotificationChannel(
|
||||
NOTIFY_CHANNEL, "ColorOS 通知图标增强 - 版本更新",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
).apply { enableLights(false) }
|
||||
)
|
||||
notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply {
|
||||
setShowWhen(true)
|
||||
setContentTitle("模块已更新")
|
||||
setContentText("点按通知打开模块以完成新版本激活。")
|
||||
setColor(0xFF4E8A5A.toInt())
|
||||
setAutoCancel(true)
|
||||
setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_notify_update))
|
||||
setLargeIcon(context.appIconOf(packageName)?.toBitmap())
|
||||
setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context, packageName.hashCode(),
|
||||
Intent().apply {
|
||||
component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.MainActivity")
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}.build())
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/1/28.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.AnimationDrawable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.util.ArrayMap
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.fankes.coloros.notify.utils.factory.safeOfFalse
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* 这是一个从 AOSP 源码中分离出来的功能
|
||||
*
|
||||
* 主要作用于兼容部分第三方系统修改颜色判断代码造成判断位图灰度功能失效
|
||||
*/
|
||||
object BitmapCompatTool {
|
||||
|
||||
/** 缓存已判断的结果防止卡顿 */
|
||||
private var cachedBitmapGrayscales = ArrayMap<Int, Boolean>()
|
||||
|
||||
private var tempBuffer = intArrayOf(0)
|
||||
private var tempCompactBitmap: Bitmap? = null
|
||||
private var tempCompactBitmapCanvas: Canvas? = null
|
||||
private var tempCompactBitmapPaint: Paint? = null
|
||||
private val tempMatrix = Matrix()
|
||||
|
||||
/**
|
||||
* 判断 [Drawable] 是否为灰度位图
|
||||
* @param drawable 要判断的 [Drawable]
|
||||
* @return [Boolean] 是否灰度
|
||||
*/
|
||||
fun isGrayscaleDrawable(drawable: Drawable) = safeOfFalse {
|
||||
when (drawable) {
|
||||
is BitmapDrawable -> isGrayscaleBitmap(drawable.bitmap)
|
||||
is AnimationDrawable -> !(drawable.numberOfFrames <= 0 || !isGrayscaleBitmap(drawable.getFrame(0).toBitmap()))
|
||||
is VectorDrawable -> true
|
||||
else -> isGrayscaleBitmap(drawable.toBitmap())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 [Bitmap] 是否为灰度位图
|
||||
* @param bitmap 要判断的位图
|
||||
* @return [Boolean] 是否灰度
|
||||
*/
|
||||
private fun isGrayscaleBitmap(bitmap: Bitmap) =
|
||||
cachedBitmapGrayscales[bitmap.generationId] ?: let {
|
||||
var height = bitmap.height
|
||||
var width = bitmap.width
|
||||
if (height > 64 || width > 64) {
|
||||
if (tempCompactBitmap == null) {
|
||||
tempCompactBitmap = Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888)
|
||||
.also { tempCompactBitmapCanvas = Canvas(it) }
|
||||
tempCompactBitmapPaint = Paint(Paint.FILTER_BITMAP_FLAG).apply { isFilterBitmap = true }
|
||||
}
|
||||
tempMatrix.reset()
|
||||
tempMatrix.setScale(64f / width, 64f / height, 0f, 0f)
|
||||
tempCompactBitmapCanvas?.drawColor(0, PorterDuff.Mode.SRC)
|
||||
tempCompactBitmapCanvas?.drawBitmap(bitmap, tempMatrix, tempCompactBitmapPaint)
|
||||
height = 64
|
||||
width = 64
|
||||
}
|
||||
val size = height * width
|
||||
ensureBufferSize(size)
|
||||
tempCompactBitmap?.getPixels(tempBuffer, 0, width, 0, 0, width, height)
|
||||
for (i in 0 until size)
|
||||
if (isGrayscaleColor(tempBuffer[i]).not()) {
|
||||
cachedBitmapGrayscales[bitmap.generationId] = false
|
||||
return@let false
|
||||
}
|
||||
cachedBitmapGrayscales[bitmap.generationId] = true
|
||||
true
|
||||
}
|
||||
|
||||
/**
|
||||
* 提纯 [Bitmap] 颜色判断灰度
|
||||
* @param color 颜色
|
||||
* @return [Boolean] 是否灰度
|
||||
*/
|
||||
private fun isGrayscaleColor(color: Int): Boolean {
|
||||
if (color shr 24 and 255 < 50) return true
|
||||
val r = color shr 16 and 255
|
||||
val g = color shr 8 and 255
|
||||
val b = color and 255
|
||||
return !(abs(r - g) >= 20 || abs(r - b) >= 20 || abs(g - b) >= 20)
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算字节数组
|
||||
* @param size 大小
|
||||
*/
|
||||
private fun ensureBufferSize(size: Int) {
|
||||
if (tempBuffer.size < size) tempBuffer = IntArray(size)
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,20 +18,32 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/20.
|
||||
* This file is created by fankes on 2022/3/20.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import okhttp3.*
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.icu.util.Calendar
|
||||
import android.icu.util.TimeZone
|
||||
import com.fankes.coloros.notify.utils.factory.isNetWorkSuccess
|
||||
import com.fankes.coloros.notify.utils.factory.openBrowser
|
||||
import com.fankes.coloros.notify.utils.factory.openSelfSetting
|
||||
import com.fankes.coloros.notify.utils.factory.runInSafe
|
||||
import com.fankes.coloros.notify.utils.factory.showDialog
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.io.Serializable
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 获取 Github Release 最新版本工具类
|
||||
* 获取 GitHub Release 最新版本工具类
|
||||
*/
|
||||
object GithubReleaseTool {
|
||||
|
||||
@@ -45,9 +57,9 @@ object GithubReleaseTool {
|
||||
* 获取最新版本信息
|
||||
* @param context 实例
|
||||
* @param version 当前版本
|
||||
* @param it 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
|
||||
* @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
|
||||
*/
|
||||
fun checkingForUpdate(context: Context, version: String, it: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
|
||||
fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
|
||||
OkHttpClient().newBuilder().build().newCall(
|
||||
Request.Builder()
|
||||
.url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest")
|
||||
@@ -57,23 +69,23 @@ object GithubReleaseTool {
|
||||
override fun onFailure(call: Call, e: IOException) {}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) = runInSafe {
|
||||
JSONObject(response.body?.string() ?: "").apply {
|
||||
JSONObject(response.body.string()).apply {
|
||||
GithubReleaseBean(
|
||||
name = getString("name"),
|
||||
htmlUrl = getString("html_url"),
|
||||
content = getString("body"),
|
||||
date = getString("published_at").replace(oldValue = "T", newValue = " ").replace(oldValue = "Z", newValue = "")
|
||||
date = getString("published_at").localTime()
|
||||
).apply {
|
||||
fun showUpdate() = context.showDialog {
|
||||
title = "最新版本 $name"
|
||||
msg = "发布于 $date\n\n" +
|
||||
"更新日志\n\n" + content
|
||||
"更新日志\n\n" + content
|
||||
confirmButton(text = "更新") { context.openBrowser(htmlUrl) }
|
||||
cancelButton()
|
||||
}
|
||||
if (name != version) (context as? Activity?)?.runOnUiThread {
|
||||
showUpdate()
|
||||
it(name) { showUpdate() }
|
||||
result(name) { showUpdate() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,10 +96,10 @@ object GithubReleaseTool {
|
||||
/**
|
||||
* 检查网络连接情况
|
||||
* @param context 实例
|
||||
* @param it 已连接回调
|
||||
* @param result 已连接回调
|
||||
*/
|
||||
private fun checkingInternetConnect(context: Context, it: () -> Unit) = runInSafe {
|
||||
if (isNetWorkSuccess)
|
||||
private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe {
|
||||
if (context.isNetWorkSuccess)
|
||||
OkHttpClient().newBuilder().build().newCall(
|
||||
Request.Builder()
|
||||
.url("https://www.baidu.com")
|
||||
@@ -98,7 +110,7 @@ object GithubReleaseTool {
|
||||
(context as? Activity?)?.runOnUiThread {
|
||||
context.showDialog {
|
||||
title = "网络不可用"
|
||||
msg = "模块的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
|
||||
msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
|
||||
confirmButton(text = "去开启") { context.openSelfSetting() }
|
||||
cancelButton()
|
||||
noCancelable()
|
||||
@@ -107,13 +119,25 @@ object GithubReleaseTool {
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) = runInSafe {
|
||||
(context as? Activity?)?.runOnUiThread { runInSafe { it() } }
|
||||
(context as? Activity?)?.runOnUiThread { runInSafe { result() } }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Github Release bean
|
||||
* 格式化时间为本地时区
|
||||
* @return [String] 本地时区时间
|
||||
*/
|
||||
private fun String.localTime() = replace("T", " ").replace("Z", "").let {
|
||||
runCatching {
|
||||
val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
|
||||
val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
|
||||
local.format(current.parse(it))
|
||||
}.getOrNull() ?: it
|
||||
}
|
||||
|
||||
/**
|
||||
* GitHub Release bean
|
||||
* @param name 版本名称
|
||||
* @param htmlUrl 网页地址
|
||||
* @param content 更新日志
|
||||
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <p>
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/2/3.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
import android.content.Context
|
||||
import com.fankes.coloros.notify.utils.factory.showDialog
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* I18n 适配警告提示工具类
|
||||
*/
|
||||
object I18nWarnTool {
|
||||
|
||||
/** 推广已读存储键值 */
|
||||
private val LOCALE_WARN_READED = PrefsData("locale_warn_readed", false)
|
||||
|
||||
/**
|
||||
* 检查并显示 I18n 适配警告对话框
|
||||
* @param context 实例
|
||||
*/
|
||||
fun checkingOrShowing(context: Context) {
|
||||
fun saveReaded() = context.prefs().edit { put(LOCALE_WARN_READED, value = true) }
|
||||
if (Locale.getDefault().language.startsWith("zh").not() && context.prefs().get(LOCALE_WARN_READED).not())
|
||||
context.showDialog {
|
||||
title = "Notice of I18n Support"
|
||||
msg = "This Xposed Module is only for Chinese and the Chinese region.\n\n" +
|
||||
"Currently, there will be no internationalization adaptation.\n\n" +
|
||||
"There may be plans for internationalization adaptation in the future, so stay tuned."
|
||||
confirmButton(text = "Got It") { saveReaded() }
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,7 +18,7 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/20.
|
||||
* This file is created by fankes on 2022/3/20.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
@@ -29,13 +29,18 @@ import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.fankes.coloros.notify.const.Const
|
||||
import com.fankes.coloros.notify.R
|
||||
import com.fankes.coloros.notify.hook.HookEntry
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import com.fankes.coloros.notify.utils.factory.appIconOf
|
||||
import com.fankes.coloros.notify.utils.factory.appNameOf
|
||||
import com.fankes.coloros.notify.utils.factory.isDebugApp
|
||||
import com.fankes.coloros.notify.utils.factory.isSystemApp
|
||||
import com.fankes.coloros.notify.utils.factory.runInSafe
|
||||
import com.fankes.coloros.notify.utils.factory.stampToDate
|
||||
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
|
||||
|
||||
/**
|
||||
* 通知图标适配推送通知类
|
||||
@@ -44,71 +49,14 @@ import com.fankes.coloros.notify.utils.factory.*
|
||||
*/
|
||||
object IconAdaptationTool {
|
||||
|
||||
/** 当前模块的包名 */
|
||||
private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID
|
||||
|
||||
/** 推送通知的渠道名称 */
|
||||
private const val NOTIFY_CHANNEL = "notifyRuleSupportId"
|
||||
|
||||
/** 已过期的日期 */
|
||||
private val outDateLimits = HashSet<String>()
|
||||
|
||||
/**
|
||||
* 使用的小图标
|
||||
* @return [Bitmap]
|
||||
*/
|
||||
private val smallIcon by lazy {
|
||||
("iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAG\n" +
|
||||
"x2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkP\n" +
|
||||
"SJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG\n" +
|
||||
"9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDYgNzkuZGF\n" +
|
||||
"iYWNiYiwgMjAyMS8wNC8xNC0wMDozOTo0NCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6\n" +
|
||||
"cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gP\n" +
|
||||
"HJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYW\n" +
|
||||
"RvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1\n" +
|
||||
"lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90\n" +
|
||||
"b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuM\n" +
|
||||
"C9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cG\n" +
|
||||
"UvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI\n" +
|
||||
"yLjQgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTAzLTIzVDAyOjU1OjM2\n" +
|
||||
"KzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wMy0yM1QwMzowMDoyMSswODowMCIge\n" +
|
||||
"G1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0wMy0yM1QwMzowMDoyMSswODowMCIgZGM6Zm9ybW\n" +
|
||||
"F0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0N\n" +
|
||||
"Qcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlp\n" +
|
||||
"ZDpiZGY1YWY2NS1lYjYxLTRiOGUtOTk1NS01OTE5OThkN2ExNDAiIHhtcE1NOkRvY3VtZ\n" +
|
||||
"W50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2MDFlYjQ1OC01ZTdlLWFjNDYtODA0Mi\n" +
|
||||
"1iNWJmYTFhYWQwNGMiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYTU\n" +
|
||||
"4YTA1ZS04OTU1LTQyNzItODg2NC0xNmI5MWI0YzcxMmUiPiA8eG1wTU06SGlzdG9yeT4g\n" +
|
||||
"PHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0Y\n" +
|
||||
"W5jZUlEPSJ4bXAuaWlkOmNhNThhMDVlLTg5NTUtNDI3Mi04ODY0LTE2YjkxYjRjNzEyZS\n" +
|
||||
"Igc3RFdnQ6d2hlbj0iMjAyMi0wMy0yM1QwMjo1NTozNiswODowMCIgc3RFdnQ6c29mdHd\n" +
|
||||
"hcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIyLjQgKE1hY2ludG9zaCkiLz4gPHJkZjps\n" +
|
||||
"aSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjg5Y\n" +
|
||||
"mIxNTI1LWVjYmMtNGY2NC1iZTRlLWU1N2EyNjQ1NDE1YyIgc3RFdnQ6d2hlbj0iMjAyMi\n" +
|
||||
"0wMy0yM1QwMjo1NzoxMCswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGh\n" +
|
||||
"vdG9zaG9wIDIyLjQgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjps\n" +
|
||||
"aSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJkZ\n" +
|
||||
"jVhZjY1LWViNjEtNGI4ZS05OTU1LTU5MTk5OGQ3YTE0MCIgc3RFdnQ6d2hlbj0iMjAyMi\n" +
|
||||
"0wMy0yM1QwMzowMDoyMSswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGh\n" +
|
||||
"vdG9zaG9wIDIyLjQgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6\n" +
|
||||
"U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+I\n" +
|
||||
"DwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+QdGsggAAA35JREFUaIHtmE2IVW\n" +
|
||||
"Ucxn9z0Ukm7cMSo1AxUwjNIclLRVSL0CAoyD5oYwS1iogWGW0rwQpcJNjCRdKmTR+SBhF\n" +
|
||||
"BWWZlZllmOgsT/EqHKRuqMVv8XJy5zPGd95x75hyh03Ae+C/O//2/z3Oee97P26MyGdD6\n" +
|
||||
"r1/gQqExUjc0RuqGxkjd0BipGxojdcOkMTKlS/tCYBXQBvqBWcA/wI/AD8AW4NML9C79w\n" +
|
||||
"APAYmAOcCUwCBwCvgTeB37J7K3Goq1utRgG1NUZPEXibnVnQa231CUxnhjxmoKkId5WWx\n" +
|
||||
"M08XpJrSe7GXmlJHEH36l9oUgketRPKmqtS3P2OHaxWgO8HBl9vwPbgM+AU8B0YAnJ3Fk\n" +
|
||||
"Uqd8HLAfO5MyHz4HbIvkh4ENgJ/Dn6DxZCdxFfGF6GngtPUeuz3D9qnpZzi/7mDoS6fe9\n" +
|
||||
"Oi1SP0XdnqH1vDo9Q2eeujmj33WmhtbuSMFTOQbSsUg9Gem/3/HDbEuk7ox6c0GtZyP9d\n" +
|
||||
"3WMLIg0vlGQuBPz1cEIzx51aqouxN/qsglqvRnhWYj6UpAcVnsnSN75/MciIntTfC+m8r\n" +
|
||||
"+qi0vo9Kp/BBrrUD8KkutLkHdijvpbxMy3jn2Z1eoL6uUVdDYE/NtRjwfJ+yoIoF6rnoi\n" +
|
||||
"Y2VORNx33B9wHWsDMYEk7m7NsFsEhYBlwMsjfCOwFplbkBzgdPPe2gOEg2e38VQQnSMwM\n" +
|
||||
"BvmlwBfAtIr8FwXPIy1gf5BsVxS5ieTQd5zEzFDQvhzYBfRU0OgPnk/j+GPJYMlx26d+P\n" +
|
||||
"MpxNjXX5hlfmneYbJBltAYCrk2YnCZDnHeOKRAXqz8FHMOp9mtMltsQu0uYeTzC085yqL\n" +
|
||||
"qqIPFMdV+k/46gbr7xpflrz98086Kt/hv0P6JjR5RbIwKqz5h/NF/p+OVb9bA6O1I/N6P\n" +
|
||||
"+Z/WWLiYezXjHO9JGMBlOMRxW16r3mhwn7jS5D2Rdho6ps3JeKGuYaXKnedhkL5qhLlWf\n" +
|
||||
"UL/KqN/Y4Q1FNmd0KIqj6tU5JtJmjnThGu7S/k6aMyayvpSF5FfL+xJhXKF+U1JrU8iXJ\n" +
|
||||
"bLC7M8Z4pTJXJrIypOO59ShgloH1YdiPOkbYgy3A48ANwBXAZcCIyQ79gDwAfAe8FeFzQ\n" +
|
||||
"3gEuBB4B5gATAb6BvlPUpy63wX2JpF0M3I/waT5g+6xkjd0BipGxojdUNjpG5ojNQNjZG\n" +
|
||||
"64Rx5J7QeMy++3AAAAABJRU5ErkJggg==").bitmap
|
||||
}
|
||||
/** 已过期的时间 */
|
||||
private val outTimeLimits = HashSet<String>()
|
||||
|
||||
/**
|
||||
* 推送新 APP 安装适配通知
|
||||
@@ -116,6 +64,8 @@ object IconAdaptationTool {
|
||||
* @param packageName 安装的 APP 包名
|
||||
*/
|
||||
fun pushNewAppSupportNotify(context: Context, packageName: String) {
|
||||
if (packageName.startsWith("com.google.android.trichromelibrary")) return
|
||||
if (context.isSystemApp(packageName) || context.isDebugApp(packageName)) return
|
||||
context.getSystemService(NotificationManager::class.java)?.apply {
|
||||
createNotificationChannel(
|
||||
NotificationChannel(
|
||||
@@ -125,24 +75,24 @@ object IconAdaptationTool {
|
||||
)
|
||||
notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply {
|
||||
setShowWhen(true)
|
||||
setContentTitle("您已安装 ${context.findAppName(packageName)}")
|
||||
setContentTitle("您已安装 ${context.appNameOf(packageName)}")
|
||||
setContentText("尚未适配此应用,点按打开在线规则。")
|
||||
setColor(0xFF2993F0.toInt())
|
||||
setAutoCancel(true)
|
||||
setSmallIcon(Icon.createWithBitmap(smallIcon))
|
||||
setLargeIcon(context.findAppIcon(packageName)?.toBitmap())
|
||||
setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_unsupported))
|
||||
setLargeIcon(context.appIconOf(packageName)?.toBitmap())
|
||||
setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context, packageName.hashCode(),
|
||||
Intent().apply {
|
||||
component = ComponentName(
|
||||
Const.MODULE_PACKAGE_NAME,
|
||||
"${Const.MODULE_PACKAGE_NAME}.ui.activity.ConfigureActivity"
|
||||
MODULE_PACKAGE_NAME,
|
||||
"$MODULE_PACKAGE_NAME.ui.activity.ConfigureActivity"
|
||||
)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}.apply {
|
||||
putExtra("isNewAppSupport", true)
|
||||
putExtra("appName", context.findAppName(packageName))
|
||||
putExtra("appName", context.appNameOf(packageName))
|
||||
putExtra("pkgName", packageName)
|
||||
}, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
@@ -162,27 +112,20 @@ object IconAdaptationTool {
|
||||
|
||||
/**
|
||||
* 自动更新通知图标优化在线规则
|
||||
*
|
||||
* 一天执行一次
|
||||
* @param context 实例
|
||||
* @param timeSet 设定的时间
|
||||
*/
|
||||
fun prepareAutoUpdateIconRule(context: Context, timeSet: String) = runInSafe {
|
||||
System.currentTimeMillis().also {
|
||||
if (it.stampToDate(format = "HH:mm") == timeSet && (outDateLimits.isEmpty() || outDateLimits.none { each ->
|
||||
each == it.stampToDate(format = "yyyy-MM-dd")
|
||||
})) {
|
||||
outDateLimits.add(it.stampToDate(format = "yyyy-MM-dd"))
|
||||
context.startActivity(
|
||||
Intent().apply {
|
||||
component = ComponentName(
|
||||
Const.MODULE_PACKAGE_NAME,
|
||||
"${Const.MODULE_PACKAGE_NAME}.ui.activity.auto.NotifyIconRuleUpdateActivity"
|
||||
)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
)
|
||||
}
|
||||
val nowTime = it.stampToDate(format = "HH:mm")
|
||||
if (timeSet != nowTime || outTimeLimits.any { e -> e == nowTime }) return
|
||||
outTimeLimits.add(nowTime)
|
||||
context.startActivity(
|
||||
Intent().apply {
|
||||
component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.auto.NotifyIconRuleUpdateActivity")
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,9 +18,9 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/2/25.
|
||||
* This file is created by fankes on 2022/2/25.
|
||||
*/
|
||||
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "DEPRECATION", "IMPLICIT_CAST_TO_ANY")
|
||||
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "IMPLICIT_CAST_TO_ANY")
|
||||
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
@@ -39,24 +39,31 @@ import androidx.core.content.getSystemService
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.fankes.coloros.notify.R
|
||||
import com.fankes.coloros.notify.data.DataConst
|
||||
import com.fankes.coloros.notify.const.IconRuleSourceSyncType
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.fankes.coloros.notify.databinding.DiaSourceFromBinding
|
||||
import com.fankes.coloros.notify.databinding.DiaSourceFromStringBinding
|
||||
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_1
|
||||
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_2
|
||||
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_3
|
||||
import com.fankes.coloros.notify.param.IconPackParams
|
||||
import com.fankes.coloros.notify.ui.activity.ConfigureActivity
|
||||
import com.fankes.coloros.notify.utils.factory.delayedRun
|
||||
import com.fankes.coloros.notify.utils.factory.openBrowser
|
||||
import com.fankes.coloros.notify.utils.factory.safeOfNull
|
||||
import com.fankes.coloros.notify.utils.factory.showDialog
|
||||
import com.fankes.coloros.notify.utils.factory.snake
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.log.loggerD
|
||||
import okhttp3.*
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
import java.security.SecureRandom
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.*
|
||||
import javax.net.ssl.HostnameVerifier
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
|
||||
/**
|
||||
* 通知图标在线规则管理类
|
||||
@@ -70,65 +77,90 @@ object IconRuleManagerTool {
|
||||
private const val OS_TAG = "ColorOS"
|
||||
|
||||
/** 当前规则的通知图标颜色 */
|
||||
private const val OS_COLOR = 0xFF4E8A5A
|
||||
private const val OS_COLOR = 0xFF4E8A5A.toInt()
|
||||
|
||||
/** 同步地址 - GitHub Raw (直连) */
|
||||
private const val SYNC_DIRECT_URL = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main"
|
||||
|
||||
/** 同步地址 - GitHub Raw (代理 - GitHub Proxy) */
|
||||
private const val SYNC_PROXY_1_URL = "https://ghp.ci/$SYNC_DIRECT_URL"
|
||||
|
||||
/** 同步地址 - GitHub Raw (代理 - 7ED Services) */
|
||||
private const val SYNC_PROXY_2_URL = "https://raw.githubusercontentS.com/fankes/AndroidNotifyIconAdapt/main"
|
||||
|
||||
/** 云端规则展示地址 (OS) */
|
||||
private const val RULES_TRAVELER_OS_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?notify-rules-coloros"
|
||||
|
||||
/** 云端规则展示地址 (APP) */
|
||||
private const val RULES_TRAVELER_APP_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?notify-rules-app"
|
||||
|
||||
/** 请求适配通知图标优化名单地址 */
|
||||
internal const val RULES_FEEDBACK_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?feedback"
|
||||
|
||||
/** 贡献通知图标优化名单地址 */
|
||||
internal const val RULES_CONTRIBUTING_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?contribute"
|
||||
|
||||
/**
|
||||
* 从在线地址手动同步规则
|
||||
* @param context 实例
|
||||
* @param it 成功后回调
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun syncByHand(context: Context, it: () -> Unit) =
|
||||
context.showDialog {
|
||||
fun syncByHand(context: Context, callback: () -> Unit) =
|
||||
context.showDialog<DiaSourceFromBinding> {
|
||||
title = "同步列表"
|
||||
var sourceType = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY)
|
||||
var customUrl = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY_CUSTOM_URL)
|
||||
bind<DiaSourceFromBinding>().apply {
|
||||
diaSfText.apply {
|
||||
if (customUrl.isNotBlank()) {
|
||||
setText(customUrl)
|
||||
setSelection(customUrl.length)
|
||||
}
|
||||
doOnTextChanged { text, _, _, _ -> customUrl = text.toString() }
|
||||
}
|
||||
diaSfTextLin.isVisible = sourceType == TYPE_SOURCE_SYNC_WAY_3
|
||||
diaSfRd1.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_1
|
||||
diaSfRd2.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_2
|
||||
diaSfRd3.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_3
|
||||
diaSfRd1.setOnClickListener {
|
||||
diaSfRd2.isChecked = false
|
||||
diaSfRd3.isChecked = false
|
||||
diaSfTextLin.isVisible = false
|
||||
sourceType = TYPE_SOURCE_SYNC_WAY_1
|
||||
}
|
||||
diaSfRd2.setOnClickListener {
|
||||
diaSfRd1.isChecked = false
|
||||
diaSfRd3.isChecked = false
|
||||
diaSfTextLin.isVisible = false
|
||||
sourceType = TYPE_SOURCE_SYNC_WAY_2
|
||||
}
|
||||
diaSfRd3.setOnClickListener {
|
||||
diaSfRd1.isChecked = false
|
||||
diaSfRd2.isChecked = false
|
||||
diaSfTextLin.isVisible = true
|
||||
sourceType = TYPE_SOURCE_SYNC_WAY_3
|
||||
var sourceType = ConfigData.iconRuleSourceSyncType
|
||||
var customUrl = ConfigData.iconRuleSourceSyncCustomUrl
|
||||
binding.sourceUrlEdit.apply {
|
||||
if (customUrl.isNotBlank()) {
|
||||
setText(customUrl)
|
||||
setSelection(customUrl.length)
|
||||
}
|
||||
doOnTextChanged { text, _, _, _ -> customUrl = text.toString() }
|
||||
}
|
||||
binding.sourceFromTextLin.isVisible = sourceType == IconRuleSourceSyncType.CUSTOM_URL
|
||||
binding.sourceTravelerLin.isVisible = sourceType != IconRuleSourceSyncType.CUSTOM_URL
|
||||
binding.sourceRadio0.isChecked = sourceType == IconRuleSourceSyncType.GITHUB_RAW_PROXY_1
|
||||
binding.sourceRadio1.isChecked = sourceType == IconRuleSourceSyncType.GITHUB_RAW_PROXY_2
|
||||
binding.sourceRadio2.isChecked = sourceType == IconRuleSourceSyncType.GITHUB_RAW_DIRECT
|
||||
binding.sourceRadio3.isChecked = sourceType == IconRuleSourceSyncType.CUSTOM_URL
|
||||
binding.sourceRadio0.setOnClickListener {
|
||||
binding.sourceFromTextLin.isVisible = false
|
||||
binding.sourceTravelerLin.isVisible = true
|
||||
sourceType = IconRuleSourceSyncType.GITHUB_RAW_PROXY_1
|
||||
}
|
||||
binding.sourceRadio1.setOnClickListener {
|
||||
binding.sourceFromTextLin.isVisible = false
|
||||
binding.sourceTravelerLin.isVisible = true
|
||||
sourceType = IconRuleSourceSyncType.GITHUB_RAW_PROXY_2
|
||||
}
|
||||
binding.sourceRadio2.setOnClickListener {
|
||||
binding.sourceFromTextLin.isVisible = false
|
||||
binding.sourceTravelerLin.isVisible = true
|
||||
sourceType = IconRuleSourceSyncType.GITHUB_RAW_DIRECT
|
||||
}
|
||||
binding.sourceRadio3.setOnClickListener {
|
||||
binding.sourceFromTextLin.isVisible = true
|
||||
binding.sourceTravelerLin.isVisible = false
|
||||
sourceType = IconRuleSourceSyncType.CUSTOM_URL
|
||||
}
|
||||
binding.sourceTravelerOsButton.setOnClickListener { context.openBrowser(RULES_TRAVELER_OS_URL) }
|
||||
binding.sourceTravelerAppButton.setOnClickListener { context.openBrowser(RULES_TRAVELER_APP_URL) }
|
||||
confirmButton {
|
||||
context.modulePrefs.put(DataConst.SOURCE_SYNC_WAY, sourceType)
|
||||
context.modulePrefs.put(DataConst.SOURCE_SYNC_WAY_CUSTOM_URL, customUrl)
|
||||
sync(context, sourceType, customUrl, it)
|
||||
ConfigData.iconRuleSourceSyncType = sourceType
|
||||
ConfigData.iconRuleSourceSyncCustomUrl = customUrl
|
||||
sync(context, sourceType, customUrl, callback)
|
||||
}
|
||||
cancelButton()
|
||||
neutralButton(text = "自定义规则") {
|
||||
context.showDialog {
|
||||
title = "自定义规则"
|
||||
val editText = bind<DiaSourceFromStringBinding>().diaSfsInputEdit.apply {
|
||||
context.showDialog<DiaSourceFromStringBinding> {
|
||||
title = "自定义规则 (调试)"
|
||||
binding.jsonRuleEdit.apply {
|
||||
requestFocus()
|
||||
invalidate()
|
||||
}
|
||||
IconPackParams(context).also { params ->
|
||||
confirmButton(text = "合并") {
|
||||
editText.text.toString().also { jsonString ->
|
||||
binding.jsonRuleEdit.text.toString().also { jsonString ->
|
||||
when {
|
||||
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> context.snake(msg = "不是有效的 JSON 数据")
|
||||
jsonString.isNotBlank() -> {
|
||||
@@ -139,20 +171,20 @@ object IconRuleManagerTool {
|
||||
)
|
||||
)
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> context.snake(msg = "请输入有效内容")
|
||||
}
|
||||
}
|
||||
}
|
||||
cancelButton(text = "覆盖") {
|
||||
editText.text.toString().also { jsonString ->
|
||||
binding.jsonRuleEdit.text.toString().also { jsonString ->
|
||||
when {
|
||||
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> context.snake(msg = "不是有效的 JSON 数据")
|
||||
jsonString.isNotBlank() -> {
|
||||
params.save(dataJson = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]")
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> context.snake(msg = "请输入有效内容")
|
||||
}
|
||||
@@ -169,23 +201,22 @@ object IconRuleManagerTool {
|
||||
* @param context 实例
|
||||
* @param sourceType 同步地址类型 - 默认自动获取已存储的键值
|
||||
* @param customUrl 自定义同步地址 - 默认自动获取已存储的键值
|
||||
* @param it 成功后回调
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun sync(
|
||||
context: Context,
|
||||
sourceType: Int = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY),
|
||||
customUrl: String = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY_CUSTOM_URL),
|
||||
it: () -> Unit
|
||||
sourceType: Int = ConfigData.iconRuleSourceSyncType,
|
||||
customUrl: String = ConfigData.iconRuleSourceSyncCustomUrl,
|
||||
callback: () -> Unit
|
||||
) {
|
||||
when (sourceType) {
|
||||
TYPE_SOURCE_SYNC_WAY_1 ->
|
||||
onRefreshing(context, url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main", it)
|
||||
TYPE_SOURCE_SYNC_WAY_2 ->
|
||||
onRefreshing(context, url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main", it)
|
||||
TYPE_SOURCE_SYNC_WAY_3 ->
|
||||
IconRuleSourceSyncType.GITHUB_RAW_PROXY_1 -> onRefreshing(context, SYNC_PROXY_1_URL, callback)
|
||||
IconRuleSourceSyncType.GITHUB_RAW_PROXY_2 -> onRefreshing(context, SYNC_PROXY_2_URL, callback)
|
||||
IconRuleSourceSyncType.GITHUB_RAW_DIRECT -> onRefreshing(context, SYNC_DIRECT_URL, callback)
|
||||
IconRuleSourceSyncType.CUSTOM_URL ->
|
||||
if (customUrl.isNotBlank())
|
||||
if (customUrl.startsWith("http://") || customUrl.startsWith("https://"))
|
||||
onRefreshingCustom(context, customUrl, it)
|
||||
onRefreshingCustom(context, customUrl, callback)
|
||||
else context.snakeOrNotify(title = "同步失败", msg = "同步地址不是一个合法的 URL")
|
||||
else context.snakeOrNotify(title = "同步失败", msg = "同步地址不能为空")
|
||||
else -> context.snakeOrNotify(title = "同步异常", msg = "同步类型错误")
|
||||
@@ -196,14 +227,14 @@ object IconRuleManagerTool {
|
||||
* 开始更新数据
|
||||
* @param context 实例
|
||||
* @param url
|
||||
* @param it 成功后回调
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
private fun onRefreshing(context: Context, url: String, it: () -> Unit) = checkingInternetConnect(context) {
|
||||
fun doParse(callback: (Boolean) -> Unit = {}) {
|
||||
private fun onRefreshing(context: Context, url: String, callback: () -> Unit) = checkingInternetConnect(context) {
|
||||
fun doParse(result: (Boolean) -> Unit = {}) {
|
||||
wait(context, url = "$url/OS/$OS_TAG/NotifyIconsSupportConfig.json") { isDone1, ctOS ->
|
||||
callback(true)
|
||||
result(true)
|
||||
wait(context, url = "$url/APP/NotifyIconsSupportConfig.json") { isDone2, ctAPP ->
|
||||
callback(false)
|
||||
result(false)
|
||||
IconPackParams(context).also { params ->
|
||||
when {
|
||||
isDone1 && isDone2 -> params.splicingJsonArray(ctOS, ctAPP).also {
|
||||
@@ -214,18 +245,17 @@ object IconRuleManagerTool {
|
||||
context.snakeOrNotify(title = "同步错误", msg = "目标地址不是有效的 JSON 数据")
|
||||
params.isCompareDifferent(it) -> {
|
||||
params.save(it)
|
||||
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
|
||||
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新") else Unit)
|
||||
}
|
||||
}
|
||||
context is AppCompatActivity ->
|
||||
context.showDialog {
|
||||
title = "连接失败"
|
||||
msg = "连接失败,错误如下:\n${if (isDone1.not()) ctOS else ctAPP}"
|
||||
confirmButton(text = "再试一次") { syncByHand(context, it) }
|
||||
confirmButton(text = "再试一次") { syncByHand(context, callback) }
|
||||
cancelButton()
|
||||
}
|
||||
else -> pushNotify(context, title = "同步地址不可用", msg = if (isDone1.not()) ctOS else ctAPP)
|
||||
@@ -248,12 +278,14 @@ object IconRuleManagerTool {
|
||||
* 开始更新数据
|
||||
* @param context 实例
|
||||
* @param url
|
||||
* @param it 成功后回调
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
private fun onRefreshingCustom(context: Context, url: String, it: () -> Unit) = checkingInternetConnect(context) {
|
||||
fun doParse(callback: () -> Unit = {}) {
|
||||
private fun onRefreshingCustom(context: Context, url: String, callback: () -> Unit) = checkingInternetConnect(context) {
|
||||
fun doParse(result: () -> Unit = {}) {
|
||||
wait(context, url) { isDone, content ->
|
||||
callback()
|
||||
result()
|
||||
/** 延迟重新回调防止对话框无法取消 */
|
||||
delayedRun { result() }
|
||||
IconPackParams(context).also { params ->
|
||||
when {
|
||||
isDone -> when {
|
||||
@@ -263,11 +295,10 @@ object IconRuleManagerTool {
|
||||
context.snakeOrNotify(title = "同步错误", msg = "目标地址不是有效的 JSON 数据")
|
||||
params.isCompareDifferent(content) -> {
|
||||
params.save(content)
|
||||
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
|
||||
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新") else Unit)
|
||||
}
|
||||
context is AppCompatActivity ->
|
||||
context.showDialog {
|
||||
@@ -293,16 +324,16 @@ object IconRuleManagerTool {
|
||||
/**
|
||||
* 检查网络连接情况
|
||||
* @param context 实例
|
||||
* @param it 已连接回调
|
||||
* @param callback 已连接回调
|
||||
*/
|
||||
private fun checkingInternetConnect(context: Context, it: () -> Unit) =
|
||||
private fun checkingInternetConnect(context: Context, callback: () -> Unit) =
|
||||
if (context is AppCompatActivity) context.showDialog {
|
||||
title = "准备中"
|
||||
progressContent = "正在检查网络连接情况"
|
||||
noCancelable()
|
||||
baseCheckingInternetConnect(context) { isDone ->
|
||||
cancel()
|
||||
if (isDone) it() else
|
||||
if (isDone) callback() else
|
||||
context.showDialog {
|
||||
title = "网络不可用"
|
||||
msg = "无法连接到互联网,请检查你当前的设备是否可以上网,且没有在手机管家中禁用本模块的联网权限。"
|
||||
@@ -319,24 +350,24 @@ object IconRuleManagerTool {
|
||||
}
|
||||
}
|
||||
} else baseCheckingInternetConnect(context) { isDone ->
|
||||
if (isDone) it() else pushNotify(context, title = "网络不可用", msg = "无法连接到互联网,无法更新通知图标规则")
|
||||
if (isDone) callback() else pushNotify(context, title = "网络不可用", msg = "网络连接失败,无法更新通知图标规则,点击重试", isRetry = true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查网络连接情况
|
||||
* @param context 实例
|
||||
* @param it 已连接回调
|
||||
* @param result 已连接回调
|
||||
*/
|
||||
private fun baseCheckingInternetConnect(context: Context, it: (Boolean) -> Unit) =
|
||||
wait(context, url = "https://www.baidu.com") { isDone, _ -> it(isDone) }
|
||||
private fun baseCheckingInternetConnect(context: Context, result: (Boolean) -> Unit) =
|
||||
wait(context, url = "https://www.baidu.com") { isDone, _ -> result(isDone) }
|
||||
|
||||
/**
|
||||
* 发送 GET 请求内容并等待
|
||||
* @param context 实例
|
||||
* @param url 请求地址
|
||||
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
|
||||
* @param result 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
|
||||
*/
|
||||
private fun wait(context: Context, url: String, it: (Boolean, String) -> Unit) = runCatching {
|
||||
private fun wait(context: Context, url: String, result: (Boolean, String) -> Unit) = runCatching {
|
||||
OkHttpClient().newBuilder().apply {
|
||||
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
|
||||
hostnameVerifier(SSLSocketClient.hostnameVerifier)
|
||||
@@ -347,23 +378,23 @@ object IconRuleManagerTool {
|
||||
.build()
|
||||
).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
(context as? Activity?)?.runOnUiThread { it(false, e.toString()) } ?: it(false, e.toString())
|
||||
(context as? Activity?)?.runOnUiThread { result(false, e.toString()) } ?: result(false, e.toString())
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
val bodyString = response.body?.string() ?: ""
|
||||
(context as? Activity?)?.runOnUiThread { it(true, bodyString) } ?: it(true, bodyString)
|
||||
val bodyString = response.body.string()
|
||||
(context as? Activity?)?.runOnUiThread { result(true, bodyString) } ?: result(true, bodyString)
|
||||
}
|
||||
})
|
||||
}.onFailure { it(false, "URL 无效") }
|
||||
}.onFailure { result(false, "URL 无效") }
|
||||
|
||||
/**
|
||||
* 推送通知图标更新通知
|
||||
* @param context 实例
|
||||
*/
|
||||
private fun notifyRefresh(context: Context) {
|
||||
if (context !is AppCompatActivity) return
|
||||
SystemUITool.refreshSystemUI(context) { context.snake(msg = "通知图标优化名单已完成同步") }
|
||||
if (context !is AppCompatActivity) pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
|
||||
SystemUITool.refreshSystemUI(context) { if (context is AppCompatActivity) context.snake(msg = "已更新通知图标优化名单") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,8 +403,7 @@ object IconRuleManagerTool {
|
||||
* @param msg 消息内容
|
||||
*/
|
||||
private fun Context.snakeOrNotify(title: String, msg: String) {
|
||||
if (this !is AppCompatActivity)
|
||||
pushNotify(context = this, title, msg)
|
||||
if (this !is AppCompatActivity) pushNotify(context = this, title, msg)
|
||||
else snake(msg)
|
||||
}
|
||||
|
||||
@@ -382,8 +412,9 @@ object IconRuleManagerTool {
|
||||
* @param context 实例 - 类型为 [AppCompatActivity] 时将不会推送通知
|
||||
* @param title 通知标题
|
||||
* @param msg 通知消息
|
||||
* @param isRetry 是否发送重试动作 - 默认否
|
||||
*/
|
||||
private fun pushNotify(context: Context, title: String, msg: String) {
|
||||
private fun pushNotify(context: Context, title: String, msg: String, isRetry: Boolean = false) {
|
||||
if (context !is AppCompatActivity)
|
||||
context.getSystemService<NotificationManager>()?.apply {
|
||||
createNotificationChannel(
|
||||
@@ -395,7 +426,7 @@ object IconRuleManagerTool {
|
||||
notify(0, NotificationCompat.Builder(context, NOTIFY_CHANNEL).apply {
|
||||
setContentTitle(title)
|
||||
setContentText(msg)
|
||||
color = OS_COLOR.toInt()
|
||||
color = OS_COLOR
|
||||
setAutoCancel(true)
|
||||
setSmallIcon(R.drawable.ic_nf_icon_update)
|
||||
setSound(null)
|
||||
@@ -403,7 +434,9 @@ object IconRuleManagerTool {
|
||||
setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context, msg.hashCode(),
|
||||
Intent(context, ConfigureActivity::class.java).apply { putExtra("isShowUpdDialog", false) },
|
||||
Intent(context, ConfigureActivity::class.java).apply {
|
||||
if (isRetry) putExtra("isDirectUpdate", true) else putExtra("isShowUpdDialog", false)
|
||||
},
|
||||
if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
@@ -444,11 +477,11 @@ object IconRuleManagerTool {
|
||||
get() = object : X509TrustManager {
|
||||
|
||||
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) {
|
||||
loggerD(msg = "TrustX509 --> $authType")
|
||||
YLog.debug("TrustX509 --> $authType")
|
||||
}
|
||||
|
||||
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
|
||||
loggerD(msg = "TrustX509 --> $authType")
|
||||
YLog.debug("TrustX509 --> $authType")
|
||||
}
|
||||
|
||||
override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,124 +18,208 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/2/8.
|
||||
* This file is created by fankes on 2022/2/8.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext
|
||||
import com.fankes.coloros.notify.const.Const
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import android.os.Build
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.fankes.coloros.notify.const.PackageName
|
||||
import com.fankes.coloros.notify.data.ConfigData
|
||||
import com.fankes.coloros.notify.ui.activity.MainActivity
|
||||
import com.fankes.coloros.notify.utils.factory.colorOSFullVersion
|
||||
import com.fankes.coloros.notify.utils.factory.delayedRun
|
||||
import com.fankes.coloros.notify.utils.factory.execShell
|
||||
import com.fankes.coloros.notify.utils.factory.showDialog
|
||||
import com.fankes.coloros.notify.utils.factory.snake
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.factory.dataChannel
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.log.data.YLogData
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 系统界面工具
|
||||
*/
|
||||
object SystemUITool {
|
||||
|
||||
/** 宿主广播回调 */
|
||||
private var moduleHandlerCallback: ((Boolean, Boolean) -> Unit)? = null
|
||||
private val CALL_HOST_REFRESH_CACHING = ChannelData("call_host_refresh_caching", false)
|
||||
private val CALL_MODULE_REFRESH_RESULT = ChannelData("call_module_refresh_result", false)
|
||||
|
||||
/** 通知广播回调 */
|
||||
private var remindHandlerCallback: ((Boolean, Boolean) -> Unit)? = null
|
||||
/** 当前全部调试日志 */
|
||||
private var debugLogs = listOf<YLogData>()
|
||||
|
||||
/** 当前启动器实例 */
|
||||
private var launcher: ActivityResultLauncher<String>? = null
|
||||
|
||||
/**
|
||||
* 注册广播
|
||||
* @param context 实例
|
||||
* 宿主注册监听
|
||||
*/
|
||||
fun register(context: Context) {
|
||||
/** 注册广播检查模块激活状态 */
|
||||
context.registerReceiver(moduleHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_MODULE_HANDLER_RECEIVER) })
|
||||
/** 注册广播通知系统界面改变 */
|
||||
context.registerReceiver(remindHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_REMIND_HANDLER_RECEIVER) })
|
||||
}
|
||||
object Host {
|
||||
|
||||
/**
|
||||
* 取消注册广播
|
||||
* @param context 实例
|
||||
*/
|
||||
fun unregister(context: Context) {
|
||||
context.unregisterReceiver(moduleHandlerReceiver)
|
||||
context.unregisterReceiver(remindHandlerReceiver)
|
||||
/**
|
||||
* 监听系统界面刷新改变
|
||||
* @param param 实例
|
||||
* @param result 回调 - ([Boolean] 是否成功)
|
||||
*/
|
||||
fun onRefreshSystemUI(param: PackageParam, result: (Boolean) -> Boolean) {
|
||||
param.dataChannel.with { wait(CALL_HOST_REFRESH_CACHING) { put(CALL_MODULE_REFRESH_RESULT, result(it)) } }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查模块是否激活
|
||||
* @param context 实例
|
||||
* @param it 成功后回调 - ([Boolean] 是否激活,[Boolean] 是否有效)
|
||||
* @param result 成功后回调
|
||||
*/
|
||||
fun checkingActivated(context: Context, it: (Boolean, Boolean) -> Unit) {
|
||||
moduleHandlerCallback = it
|
||||
context.sendBroadcast(Intent().apply {
|
||||
action = Const.ACTION_MODULE_CHECKING_RECEIVER
|
||||
putExtra(Const.MODULE_VERSION_VERIFY_TAG, Const.MODULE_VERSION_VERIFY)
|
||||
})
|
||||
fun checkingActivated(context: Context, result: (Boolean) -> Unit) =
|
||||
context.dataChannel(PackageName.SYSTEMUI).checkingVersionEquals(result = result)
|
||||
|
||||
/**
|
||||
* 注册导出调试日志启动器到 [AppCompatActivity]
|
||||
* @param activity 实例
|
||||
*/
|
||||
fun registerExportDebugLogsLauncher(activity: AppCompatActivity) {
|
||||
launcher = activity.registerForActivityResult(ActivityResultContracts.CreateDocument("*/application")) { result ->
|
||||
runCatching {
|
||||
result?.let { e ->
|
||||
val content = "" +
|
||||
"================================================================\n" +
|
||||
" Generated by ColorOSNotifyIcon\n" +
|
||||
" Project Url: https://github.com/fankes/ColorOSNotifyIcon\n" +
|
||||
"================================================================\n\n" +
|
||||
"[Device Brand]: ${Build.BRAND}\n" +
|
||||
"[Device Model]: ${Build.MODEL}\n" +
|
||||
"[Display]: ${Build.DISPLAY}\n" +
|
||||
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
|
||||
"[Android API Level]: ${Build.VERSION.SDK_INT}\n" +
|
||||
"[ColorOS Version]: $colorOSFullVersion\n" +
|
||||
"[System Locale]: ${Locale.getDefault()}\n\n" + YLog.contents(debugLogs).trim()
|
||||
activity.contentResolver?.openOutputStream(e)?.apply { write(content.toByteArray()) }?.close()
|
||||
activity.snake(msg = "导出完成")
|
||||
} ?: activity.snake(msg = "已取消操作")
|
||||
}.onFailure { activity.snake(msg = "导出过程发生错误") }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并导出全部调试日志
|
||||
* @param context 实例
|
||||
*/
|
||||
fun obtainAndExportDebugLogs(context: Context) {
|
||||
/** 执行导出操作 */
|
||||
fun doExport() {
|
||||
context.dataChannel(PackageName.SYSTEMUI).obtainLoggerInMemoryData {
|
||||
if (it.isNotEmpty()) {
|
||||
debugLogs = it
|
||||
runCatching { launcher?.launch("coloros_notification_icons_processing_logs.log") }
|
||||
.onFailure { context.snake(msg = "启动系统文件选择器失败") }
|
||||
} else context.snake(msg = "暂无调试日志")
|
||||
}
|
||||
}
|
||||
if (YukiHookAPI.Status.isXposedModuleActive)
|
||||
context.showDialog {
|
||||
title = "导出全部调试日志"
|
||||
msg = "调试日志中会包含当前系统推送的全部通知内容,其中可能包含你的个人隐私," +
|
||||
"你可以在导出后的日志文件中选择将这些敏感信息模糊化处理再进行共享," +
|
||||
"开发者使用并查看你导出的调试日志仅为排查与修复问题,并且在之后会及时销毁这些日志。\n\n" +
|
||||
"继续导出即代表你已阅读并知悉上述内容。"
|
||||
confirmButton(text = "继续") { doExport() }
|
||||
cancelButton()
|
||||
}
|
||||
else context.snake(msg = "模块没有激活,请先激活模块")
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启系统界面
|
||||
* @param context 实例
|
||||
*/
|
||||
fun restartSystemUI(context: Context) =
|
||||
fun restartSystemUI(context: Context) {
|
||||
/** 动态刷新功能是否可用 */
|
||||
val isDynamicAvailable = ConfigData.isEnableModule && MainActivity.isModuleRegular && MainActivity.isModuleValied
|
||||
|
||||
/** 当 Root 权限获取失败时显示对话框 */
|
||||
fun showWhenAccessRootFail() =
|
||||
context.showDialog {
|
||||
title = "获取 Root 权限失败"
|
||||
msg = "当前无法获取 Root 权限,请确认你的设备已经被 Root 且同意授予 Root 权限。\n" +
|
||||
"如果你正在使用 Magisk 并安装了 Shamiko 模块," +
|
||||
"请确认当前是否正处于白名单模式。 (白名单模式将导致无法申请 Root 权限)"
|
||||
confirmButton(text = "我知道了")
|
||||
}
|
||||
context.showDialog {
|
||||
title = "重启系统界面"
|
||||
msg = "你确定要立即重启系统界面吗?\n\n" +
|
||||
"重启过程会黑屏并等待进入锁屏重新解锁。"
|
||||
"重启过程会黑屏并等待进入锁屏重新解锁。" + (if (isDynamicAvailable)
|
||||
"\n\n你也可以选择“立即生效”来动态刷新系统界面并生效当前模块设置。" else "")
|
||||
confirmButton {
|
||||
execShellSu(cmd = "pgrep systemui").also { pid ->
|
||||
execShell(cmd = "pgrep systemui").also { pid ->
|
||||
if (pid.isNotBlank())
|
||||
execShellSu(cmd = "kill -9 $pid")
|
||||
else toast(msg = "ROOT 权限获取失败")
|
||||
execShell(cmd = "kill -9 $pid")
|
||||
else showWhenAccessRootFail()
|
||||
}
|
||||
}
|
||||
cancelButton()
|
||||
if (isDynamicAvailable) neutralButton(text = "立即生效") { refreshSystemUI(context) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新系统界面状态栏与通知图标
|
||||
* @param context 实例
|
||||
* @param isRefreshCacheOnly 仅刷新缓存不刷新图标和通知改变 - 默认:否
|
||||
* @param it 成功后回调
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun refreshSystemUI(context: Context? = null, isRefreshCacheOnly: Boolean = false, it: () -> Unit = {}) = runInSafe {
|
||||
fun sendMessage() {
|
||||
(context ?: appContext).sendBroadcast(Intent().apply {
|
||||
action = Const.ACTION_REMIND_CHECKING_RECEIVER
|
||||
putExtra("isRefreshCacheOnly", isRefreshCacheOnly)
|
||||
putExtra(Const.MODULE_VERSION_VERIFY_TAG, Const.MODULE_VERSION_VERIFY)
|
||||
})
|
||||
fun refreshSystemUI(context: Context? = null, isRefreshCacheOnly: Boolean = false, callback: () -> Unit = {}) {
|
||||
/**
|
||||
* 刷新系统界面
|
||||
* @param result 回调结果
|
||||
*/
|
||||
fun doRefresh(result: (Boolean) -> Unit) {
|
||||
context?.dataChannel(PackageName.SYSTEMUI)?.with {
|
||||
wait(CALL_MODULE_REFRESH_RESULT) { result(it) }
|
||||
put(CALL_HOST_REFRESH_CACHING, isRefreshCacheOnly)
|
||||
}
|
||||
}
|
||||
if (isXposedModuleActive)
|
||||
context?.showDialog {
|
||||
title = "请稍后"
|
||||
progressContent = "正在等待系统界面刷新"
|
||||
/** 是否等待成功 */
|
||||
var isWaited = false
|
||||
/** 设置等待延迟 */
|
||||
delayedRun(ms = 5000) {
|
||||
if (isWaited) return@delayedRun
|
||||
remindHandlerCallback = null
|
||||
cancel()
|
||||
context.snake(msg = "预计响应超时,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
|
||||
}
|
||||
remindHandlerCallback = { isGrasp, isValied ->
|
||||
remindHandlerCallback = null
|
||||
cancel()
|
||||
isWaited = true
|
||||
when {
|
||||
isGrasp && !isValied ->
|
||||
context.snake(msg = "请重启系统界面以生效模块更新", actionText = "立即重启") { restartSystemUI(context) }
|
||||
else -> it()
|
||||
when {
|
||||
YukiHookAPI.Status.isXposedModuleActive && context is AppCompatActivity ->
|
||||
context.showDialog {
|
||||
title = "请稍后"
|
||||
progressContent = "正在等待系统界面刷新"
|
||||
/** 是否等待成功 */
|
||||
var isWaited = false
|
||||
/** 设置等待延迟 */
|
||||
delayedRun(ms = 5000) {
|
||||
if (isWaited) return@delayedRun
|
||||
cancel()
|
||||
context.snake(msg = "预计响应超时,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
|
||||
}
|
||||
checkingActivated(context) { isValied ->
|
||||
when {
|
||||
isValied.not() -> {
|
||||
cancel()
|
||||
isWaited = true
|
||||
context.snake(msg = "请重启系统界面以生效模块更新", actionText = "立即重启") { restartSystemUI(context) }
|
||||
}
|
||||
else -> doRefresh {
|
||||
cancel()
|
||||
isWaited = true
|
||||
callback()
|
||||
if (it.not()) context.snake(msg = "刷新失败,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
|
||||
}
|
||||
}
|
||||
}
|
||||
noCancelable()
|
||||
}
|
||||
sendMessage()
|
||||
noCancelable()
|
||||
} ?: sendMessage()
|
||||
else context?.snake(msg = "模块没有激活,更改不会生效")
|
||||
YukiHookAPI.Status.isXposedModuleActive.not() && context is AppCompatActivity -> context.snake(msg = "模块没有激活,更改不会生效")
|
||||
else -> doRefresh { callback() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,29 +227,7 @@ object SystemUITool {
|
||||
* @param context 实例
|
||||
*/
|
||||
fun showNeedRestartSnake(context: Context) =
|
||||
if (isXposedModuleActive)
|
||||
if (YukiHookAPI.Status.isXposedModuleActive)
|
||||
context.snake(msg = "设置需要重启系统界面才能生效", actionText = "立即重启") { restartSystemUI(context) }
|
||||
else context.snake(msg = "模块没有激活,更改不会生效")
|
||||
|
||||
/** 宿主广播接收器 */
|
||||
private val moduleHandlerReceiver by lazy {
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val isRegular = intent?.getBooleanExtra("isRegular", false) ?: false
|
||||
val isValied = intent?.getBooleanExtra("isValied", false) ?: false
|
||||
moduleHandlerCallback?.invoke(isRegular, isValied)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 通知广播接收器 */
|
||||
private val remindHandlerReceiver by lazy {
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val isGrasp = intent?.getBooleanExtra("isGrasp", false) ?: false
|
||||
val isValied = intent?.getBooleanExtra("isValied", false) ?: false
|
||||
remindHandlerCallback?.invoke(isGrasp, isValied)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/ColorOSNotifyIcon
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
@@ -18,16 +18,20 @@
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/1/24.
|
||||
* This file is created by fankes on 2023/9/17.
|
||||
*/
|
||||
package com.fankes.coloros.notify.hook
|
||||
@file:Suppress("unused")
|
||||
|
||||
object HookConst {
|
||||
package com.fankes.coloros.notify.wrapper
|
||||
|
||||
const val TYPE_SOURCE_SYNC_WAY_1 = 1000
|
||||
const val TYPE_SOURCE_SYNC_WAY_2 = 2000
|
||||
const val TYPE_SOURCE_SYNC_WAY_3 = 3000
|
||||
import com.fankes.coloros.notify.BuildConfig
|
||||
|
||||
const val ANDROID_PACKAGE_NAME = "android"
|
||||
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
|
||||
/**
|
||||
* 对 [BuildConfig] 的包装
|
||||
*/
|
||||
object BuildConfigWrapper {
|
||||
const val APPLICATION_ID = BuildConfig.APPLICATION_ID
|
||||
const val VERSION_NAME = BuildConfig.VERSION_NAME
|
||||
const val VERSION_CODE = BuildConfig.VERSION_CODE
|
||||
val isDebug = BuildConfig.DEBUG
|
||||
}
|
9
app/src/main/res/drawable/ic_about.xml
Normal file
9
app/src/main/res/drawable/ic_about.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150.1dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1025"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M983.8,314.4c-25.7,-60.8 -62.5,-115.4 -109.4,-162.3 -46.9,-46.9 -101.5,-83.7 -162.3,-109.4 -62.9,-26.7 -129.9,-40.2 -198.8,-40.2S377.5,16 314.5,42.7C253.8,68.4 199.1,105.2 152.3,152.2c-46.9,46.9 -83.7,101.5 -109.4,162.3 -26.7,62.9 -40.2,129.9 -40.2,198.8s13.5,135.8 40.2,198.8c25.7,60.8 62.5,115.4 109.4,162.3 46.9,46.9 101.5,83.7 162.3,109.4 62.9,26.7 129.9,40.2 198.8,40.2s135.8,-13.5 198.8,-40.2c60.8,-25.7 115.4,-62.5 162.3,-109.4 46.9,-46.9 83.7,-101.5 109.4,-162.3 26.7,-62.9 40.2,-129.9 40.2,-198.8s-13.6,-135.9 -40.3,-198.9zM550.5,768.2c0,21 -17,38 -38,38s-38,-17 -38,-38L474.5,395.6c0,-21 17,-38 38,-38s38,17 38,38v372.6zM510.8,305.5c-29.2,0 -52.7,-23.7 -52.7,-52.7 0,-29.2 23.7,-52.7 52.7,-52.7 29.2,0 52.7,23.7 52.7,52.7 0.1,29.2 -23.6,52.7 -52.7,52.7z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_back.xml
Normal file
9
app/src/main/res/drawable/ic_back.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="150.39165dp"
|
||||
android:viewportWidth="1307"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M268.7,566.5h929.6c36.3,0 72.6,-29 72.6,-72.6 0,-36.3 -29,-72.6 -72.6,-72.6H305l297.8,-297.8c29,-29 29,-72.6 0,-101.7 -29,-29 -72.6,-29 -101.7,0L72.6,450.3c-14.5,14.5 -21.8,36.3 -21.8,58.1 0,21.8 0,43.6 21.8,58.1l428.5,428.5c29,29 72.6,29 101.7,0 29,-29 29,-72.6 0,-101.7l-334.1,-326.8z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_filter.xml
Normal file
9
app/src/main/res/drawable/ic_filter.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="184.6dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1260"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M419.2,548.3L17,108.9C-23.6,64.4 13,0 79.7,0h846.5c66.6,0 103.3,64.4 62.8,108.9l-402.2,439.3v472.7c0,1.6 -1.3,2.9 -2.8,2.9a8.3,8.3 0,0 1,-5.2 -1.8l-154.3,-120a13.7,13.7 0,0 1,-5.3 -10.8L419.2,548.3zM838.2,511.9L1257.3,511.9L1257.3,597.2h-419.1L838.2,511.9zM838.2,682.5L1257.3,682.5v85.3h-419.1L838.2,682.5zM838.2,853.2L1257.3,853.2v85.3h-419.1v-85.3z" />
|
||||
</vector>
|
52
app/src/main/res/drawable/ic_function.xml
Normal file
52
app/src/main/res/drawable/ic_function.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M11,16V42"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M24,29V42"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M24,19V6"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M37,6V32"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M11,16C13.761,16 16,13.761 16,11C16,8.239 13.761,6 11,6C8.239,6 6,8.239 6,11C6,13.761 8.239,16 11,16Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M24,29C26.761,29 29,26.761 29,24C29,21.239 26.761,19 24,19C21.239,19 19,21.239 19,24C19,26.761 21.239,29 24,29Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M37,42C39.761,42 42,39.761 42,37C42,34.239 39.761,32 37,32C34.239,32 32,34.239 32,37C32,39.761 34.239,42 37,42Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_github.xml
Normal file
9
app/src/main/res/drawable/ic_github.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M512,12.6c-282.8,0 -512,229.2 -512,512 0,226.2 146.7,418.1 350.1,485.8 25.6,4.7 35,-11.1 35,-24.6 0,-12.2 -0.5,-52.5 -0.7,-95.3 -142.5,31 -172.5,-60.4 -172.5,-60.4 -23.3,-59.2 -56.8,-74.9 -56.8,-74.9 -46.5,-31.8 3.5,-31.1 3.5,-31.1 51.4,3.6 78.5,52.8 78.5,52.8 45.7,78.3 119.8,55.6 149,42.6 4.6,-33.1 17.9,-55.7 32.5,-68.5 -113.7,-12.9 -233.3,-56.9 -233.3,-253 0,-55.9 20,-101.6 52.8,-137.4 -5.3,-12.9 -22.8,-65 5,-135.5 0,0 43,-13.8 140.8,52.5 40.8,-11.4 84.6,-17 128.2,-17.2 43.5,0.2 87.3,5.9 128.3,17.2 97.7,-66.2 140.6,-52.5 140.6,-52.5 27.9,70.5 10.3,122.6 5,135.5 32.8,35.8 52.7,81.5 52.7,137.4 0,196.6 -119.8,239.9 -233.8,252.6 18.4,15.9 34.7,47 34.7,94.8 0,68.5 -0.6,123.6 -0.6,140.5 0,13.6 9.2,29.6 35.2,24.6 203.3,-67.8 349.9,-259.6 349.9,-485.8 0,-282.8 -229.2,-512 -512,-512z" />
|
||||
</vector>
|
25
app/src/main/res/drawable/ic_home.xml
Normal file
25
app/src/main/res/drawable/ic_home.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,18V42H39V18L24,6L9,18Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29V42H29V29H19Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,42H39"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round" />
|
||||
</vector>
|
30
app/src/main/res/drawable/ic_info.xml
Normal file
30
app/src/main/res/drawable/ic_info.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M24,44C29.523,44 34.523,41.761 38.142,38.142C41.761,34.523 44,29.523 44,24C44,18.477 41.761,13.477 38.142,9.858C34.523,6.239 29.523,4 24,4C18.477,4 13.477,6.239 9.858,9.858C6.239,13.477 4,18.477 4,24C4,29.523 6.239,34.523 9.858,38.142C13.477,41.761 18.477,44 24,44Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M24,11C25.381,11 26.5,12.119 26.5,13.5C26.5,14.881 25.381,16 24,16C22.619,16 21.5,14.881 21.5,13.5C21.5,12.119 22.619,11 24,11Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M24.5,34V20H23.5H22.5"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M21,34H28"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_message.xml
Normal file
10
app/src/main/res/drawable/ic_message.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m522.8,93.9c-244.2,0 -442.1,173.2 -442.1,386.9 0,122.1 64.9,230.9 165.8,301.7v195.7l193.7,-117.5c26.8,4.4 54.3,7 82.6,7 244.2,0 442.1,-173.2 442.1,-386.9C965,267.1 767.1,93.9 522.8,93.9Z"
|
||||
android:strokeWidth="1.10883" />
|
||||
</vector>
|
36
app/src/main/res/drawable/ic_modify.xml
Normal file
36
app/src/main/res/drawable/ic_modify.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M20.071,9.586L15.828,5.343C15.047,4.562 13.781,4.562 13,5.343L7.343,11C6.562,11.781 6.562,13.047 7.343,13.828L11.586,18.071"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M28.929,37.414L33.171,41.657C33.952,42.438 35.219,42.438 36,41.657L41.657,36C42.438,35.219 42.438,33.953 41.657,33.172L37.414,28.929"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M36.021,6.322L41.677,11.979A2,2 0,0 1,41.677 14.808L14.807,41.678A2,2 0,0 1,11.979 41.678L6.322,36.021A2,2 132.403,0 1,6.322 33.192L33.192,6.322A2,2 102.008,0 1,36.021 6.322z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M24,24m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M20,28m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M28,20m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
|
||||
</vector>
|
@@ -4,8 +4,7 @@
|
||||
android:tint="@color/white"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:fillColor="#ffffffff"
|
||||
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z" />
|
||||
</vector>
|
||||
</vector>
|
20
app/src/main/res/drawable/ic_notify.xml
Normal file
20
app/src/main/res/drawable/ic_notify.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M10,38V18C10,10.268 16.268,4 24,4C31.732,4 38,10.268 38,18V38M4,38H44"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M24,44C26.761,44 29,41.761 29,39V38H19V39C19,41.761 21.239,44 24,44Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
@@ -3,8 +3,7 @@
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:fillColor="#ffffffff"
|
||||
android:pathData="M192,736 L192,416C192,261.2 302,132.1 448,102.4L448,64C448,28.6 476.6,0 512,0 547.4,0 576,28.6 576,64L576,102.4C722,132.1 832,261.2 832,416L832,736 864.1,736C899.4,736 928,764.4 928,800 928,835.4 899.4,864 864.1,864L159.9,864C124.6,864 96,835.6 96,800 96,764.6 124.6,736 159.9,736L192,736ZM608,928C608,981 565,1024 512,1024 459,1024 416,981 416,928L608,928Z" />
|
||||
</vector>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_notify_update.xml
Normal file
9
app/src/main/res/drawable/ic_notify_update.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="50dp"
|
||||
android:height="50dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M906.4,791.6a247.9,247.9 0,0 0,-98.1 -186.5L808.3,399.4c0,-118.8 -85.1,-218.9 -197.4,-242.5v-4.7c0,-54.2 -44.5,-98.6 -98.8,-98.6 -54.3,0 -98.8,44.4 -98.8,98.6v4.7C300.8,180.5 215.7,280.4 215.7,399.4v205.8a247.7,247.7 0,0 0,-98.1 186.5h98.1v0.6h592.5v-0.6h98.1v-0.1zM491.1,970.4h24.5c64.3,0 117.8,-48.5 125.6,-110.7L383,859.8a126.8,126.8 0,0 0,125.6 110.7h-17.5z" />
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_page_bottom.xml
Normal file
12
app/src/main/res/drawable/ic_page_bottom.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m563.5,143.3m-41,0v0q-41,0 -41,41v573.4q0,41 41,41v0q41,0 41,-41v-573.4q0,-41 -41,-41z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M521.5,796.2 L318.2,593a41,41 0,1 0,-57.9 57.9l231.7,231.7a41,41 0,0 0,58.4 0.5L782.2,651.4a41,41 0,1 0,-57.9 -57.9z" />
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_page_top.xml
Normal file
12
app/src/main/res/drawable/ic_page_top.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m563.5,895.1m-41,0v0q-41,0 -41,-41v-573.4q0,-41 41,-41v0q41,0 41,41v573.4q0,41 -41,41z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M521.5,242.2 L318.2,445.4a41,41 0,1 1,-57.9 -57.9l231.7,-231.7a41,41 0,0 1,58.4 -0.5l231.7,231.7a41,41 0,1 1,-57.9 57.9z" />
|
||||
</vector>
|
13
app/src/main/res/drawable/ic_preference.xml
Normal file
13
app/src/main/res/drawable/ic_preference.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M44,16C44,22.627 38.627,28 32,28C29.973,28 28.064,27.497 26.39,26.61L9,44L4,39L21.39,21.61C20.503,19.936 20,18.027 20,16C20,9.373 25.373,4 32,4C34.027,4 35.936,4.502 37.61,5.39L30,13L35,18L42.61,10.39C43.498,12.064 44,13.973 44,16Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
30
app/src/main/res/drawable/ic_restart.xml
Normal file
30
app/src/main/res/drawable/ic_restart.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="4800"
|
||||
android:viewportHeight="4800">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M2270.9,56.1L2527.1,56.1A200,307.5 0,0 1,2727.1 363.6L2727.1,1098.6A200,307.5 0,0 1,2527.1 1406.1L2270.9,1406.1A200,307.5 0,0 1,2070.9 1098.6L2070.9,363.6A200,307.5 0,0 1,2270.9 56.1z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M2270.9,3406.1L2527.1,3406.1A200,307.5 0,0 1,2727.1 3713.6L2727.1,4448.6A200,307.5 0,0 1,2527.1 4756.1L2270.9,4756.1A200,307.5 0,0 1,2070.9 4448.6L2070.9,3713.6A200,307.5 0,0 1,2270.9 3406.1z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M646.7,835L827.8,653.9A200,307.5 135,0 1,1186.7 729.9L1706.4,1249.6A200,307.5 135,0 1,1782.4 1608.5L1601.3,1789.6A307.5,200 45,0 1,1242.4 1713.6L722.7,1193.9A307.5,200 45,0 1,646.7 835z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M3015.5,3203.8L3196.7,3022.7A200,307.5 135,0 1,3555.5 3098.7L4075.2,3618.4A307.5,200 45,0 1,4151.3 3977.3L3970.1,4158.4A307.5,200 45,0 1,3611.3 4082.4L3091.5,3562.7A307.5,200 45,0 1,3015.5 3203.8z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M49,2534.2L49,2278A200,307.5 90,0 1,356.5 2078L1091.5,2078A200,307.5 90,0 1,1399 2278L1399,2534.2A200,307.5 90,0 1,1091.5 2734.2L356.5,2734.2A200,307.5 90,0 1,49 2534.2z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M3399,2534.2L3399,2278.1A200,307.5 90,0 1,3706.5 2078.1L4441.5,2078.1A200,307.5 90,0 1,4749 2278.1L4749,2534.2A200,307.5 90,0 1,4441.5 2734.2L3706.5,2734.2A200,307.5 90,0 1,3399 2534.2z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M827.8,4158.4L646.7,3977.3A307.5,200 135,0 1,722.7 3618.4L1242.4,3098.7A200,307.5 45,0 1,1601.3 3022.7L1782.4,3203.8A200,307.5 45,0 1,1706.4 3562.7L1186.7,4082.4A200,307.5 45,0 1,827.8 4158.4z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M3196.7,1789.6L3015.5,1608.4A307.5,200 135,0 1,3091.5 1249.6L3611.3,729.9A307.5,200 135,0 1,3970.1 653.9L4151.3,835A200,307.5 45,0 1,4075.2 1193.9L3555.5,1713.6A200,307.5 45,0 1,3196.7 1789.6z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_success.xml
Normal file
9
app/src/main/res/drawable/ic_success.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1008.7"
|
||||
android:viewportHeight="1008.7">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M504.4,0C226.3,0 0,226.3 0,504.4 0,782.5 226.3,1008.7 504.4,1008.7c278.1,0 504.4,-226.3 504.4,-504.4C1008.7,226.3 782.5,0 504.4,0ZM786.6,407.7 L458.6,743.9c-7.8,8 -18.6,12.6 -29.8,12.6h-0.2c-11.1,0 -21.8,-4.4 -29.7,-12.3L222.5,567.9c-16.4,-16.4 -16.4,-43 0,-59.4 16.4,-16.4 43,-16.4 59.4,0L428.2,654.8 726.5,348.9c16.3,-16.6 42.9,-16.9 59.4,-0.7 16.6,16.2 16.9,42.8 0.7,59.4z" />
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_sync.xml
Normal file
12
app/src/main/res/drawable/ic_sync.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M628.5,544a224,224 0,0 0,-96 116.5l60.2,21.8a160,160 0,0 1,68.8 -83.2,152.6 152.6,0 0,1 117.4,-16 154.9,154.9 0,0 1,92.5 69.8l-39.4,28.8 116.2,32 11.8,-121.6 -35.8,25.3A217.9,217.9 0,0 0,628.5 544zM810.9,875.5a150.4,150.4 0,0 1,-118.1 9.6,155.8 155.8,0 0,1 -88.6,-74.6l40,-25.3 -114.2,-39 -17.9,121.6 37.1,-23.4A217.9,217.9 0,0 0,672 946.2a224,224 0,0 0,67.8 10.9,215 215,0 0,0 99.5,-24.6 221.1,221.1 0,0 0,111.4 -136l-61.4,-17.9a155.2,155.2 0,0 1,-78.4 97z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M736,448a285.1,285.1 0,0 1,138.6 35.5L874.6,119a48,48 0,0 0,-48 -48h-640a48,48 0,0 0,-48 48v768a48,48 0,0 0,48 48h341.8A288,288 0,0 1,736 448zM314.6,311.4h320v64h-320zM314.6,439.4h192v64h-192z" />
|
||||
</vector>
|
@@ -4,8 +4,7 @@
|
||||
android:tint="@color/white"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:fillColor="#ffffffff"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM15.29,16.71L11,12.41V7h2v4.59l3.71,3.71L15.29,16.71z" />
|
||||
</vector>
|
||||
</vector>
|
34
app/src/main/res/drawable/ic_theme.xml
Normal file
34
app/src/main/res/drawable/ic_theme.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M37,37C39.209,37 41,35.209 41,33C41,31.527 39.667,29.527 37,27C34.333,29.527 33,31.527 33,33C33,35.209 34.791,37 37,37Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M20.854,5.504L24.389,9.04"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M23.682,8.333L8.125,23.889L19.439,35.203L34.995,19.646L23.682,8.333Z"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M12,20.073L28.961,25.65"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M4,43H44"
|
||||
android:strokeWidth="4"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round" />
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_unsupported.xml
Normal file
12
app/src/main/res/drawable/ic_unsupported.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="52dp"
|
||||
android:height="52dp"
|
||||
android:viewportWidth="739.56"
|
||||
android:viewportHeight="739.56">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m207.74,575.04c-105.24,0 -192,-86.76 -192,-192 0,-105.24 86.76,-192 192,-192 36.98,0 72.53,9.96 103.82,29.87 12.8,8.53 17.07,25.6 8.53,39.82 -8.53,12.8 -25.6,17.07 -39.82,8.53 -21.33,-14.22 -46.93,-21.33 -72.53,-21.33 -73.96,0 -135.11,61.16 -135.11,135.11 0,73.96 61.16,135.11 135.11,135.11 73.96,0 135.11,-61.16 135.11,-135.11 0,-15.64 12.8,-28.44 28.44,-28.44 15.64,0 28.44,12.8 28.44,28.44 0,105.24 -86.76,192 -192,192z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m534.85,575.04c-39.82,0 -78.22,-11.38 -109.51,-34.13 -12.8,-8.53 -15.64,-27.02 -7.11,-39.82 8.53,-12.8 27.02,-15.64 39.82,-7.11 22.76,15.64 49.78,24.18 76.8,24.18 73.96,0 135.11,-61.16 135.11,-135.11 0,-73.96 -61.16,-135.11 -135.11,-135.11 -73.96,0 -135.11,61.16 -135.11,135.11 0,15.64 -12.8,28.44 -28.44,28.44 -15.64,0 -28.44,-12.8 -28.44,-28.44 0,-105.24 86.76,-192 192,-192 105.24,0 192,85.33 192,192 0,105.24 -86.76,192 -192,192z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_warn.xml
Normal file
9
app/src/main/res/drawable/ic_warn.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m512,794a44.8,44.8 0,1 1,44.8 -44.8,44.8 44.8,0 0,1 -44.8,44.8zM471.9,230.8a40.1,40.1 0,0 1,80.2 0v369.1a40.1,40.1 0,0 1,-79.8 0zM512,0A512,512 0,1 0,1024 512,512 512,0 0,0 512,0Z" />
|
||||
</vector>
|
@@ -25,7 +25,7 @@
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:src="@mipmap/ic_back"
|
||||
android:src="@drawable/ic_back"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="返回" />
|
||||
|
||||
@@ -59,19 +59,18 @@
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/config_title_up"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@mipmap/ic_page_top"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:src="@drawable/ic_page_top"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="滚动到顶部" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/config_title_down"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@mipmap/ic_page_bottom"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:layout_marginEnd="13dp"
|
||||
android:src="@drawable/ic_page_bottom"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="滚动到底部" />
|
||||
|
||||
@@ -80,7 +79,7 @@
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@mipmap/ic_filter"
|
||||
android:src="@drawable/ic_filter"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="按条件过滤" />
|
||||
|
||||
@@ -89,7 +88,7 @@
|
||||
android:layout_width="23dp"
|
||||
android:layout_height="23dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@mipmap/ic_sync"
|
||||
android:src="@drawable/ic_sync"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="同步列表" />
|
||||
</LinearLayout>
|
||||
@@ -111,7 +110,7 @@
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@mipmap/ic_about"
|
||||
android:src="@drawable/ic_about"
|
||||
android:tint="@color/colorTextDark" />
|
||||
|
||||
<TextView
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user