mirror of
https://github.com/fankes/ColorOSNotifyIcon.git
synced 2025-09-08 03:24:11 +08:00
Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
66f309dc91 | |||
d4f004894e | |||
f2e27c2702 |
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -11,7 +11,7 @@
|
||||
<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/activity_main.xml" value="0.29182608695652174" />
|
||||
<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" />
|
||||
|
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.
|
43
README.md
43
README.md
@@ -2,45 +2,59 @@
|
||||
|
||||
[](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/fankes/ColorOSNotifyIcon/releases)
|
||||
[](https://github.com/Xposed-Modules-Repo/com.fankes.coloros.notify/releases)
|
||||
[](https://t.me/XiaofangInternet)
|
||||
[](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/>
|
||||
Optimize notification icons for ColorOS and adapt to native notification icon specifications.
|
||||
|
||||
为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeUI。
|
||||
|
||||
# Developer
|
||||
## Developer
|
||||
|
||||
[酷安 @星夜不荟](http://www.coolapk.com/u/876977)
|
||||
|
||||
# 适配说明
|
||||
## 适配说明
|
||||
|
||||
- 此模块仅支持 <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),从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
|
||||
[Release](https://github.com/fankes/ColorOSNotifyIcon/releases)、[蓝奏云](https://fankes.lanzouy.com/b030rvjyf) 及**酷安应用市场**
|
||||
,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
|
||||
|
||||
# 贡献通知图标优化名单
|
||||
## 贡献通知图标优化名单
|
||||
|
||||
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。<br/>
|
||||
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。
|
||||
|
||||
- [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
|
||||
|
||||
# 历史背景
|
||||
## 历史背景
|
||||
|
||||
继 MIUI 之后的第二大系统 ColorOS 虽然支持原生通知图标,但是第三方推送五颜六色的图标系统并没有做适配,甚至系统自己的图标都是彩色的,极其不友好。
|
||||
|
||||
继 MIUI 之后的第二大系统 ColorOS 虽然支持原生通知图标,但是第三方推送五颜六色的图标系统并没有做适配,甚至系统自己的图标都是彩色的,极其不友好。<br/>
|
||||
而且从 ColorOS 12 开始,原生图标丢失了着色属性,这也是一种对原生 Android 生态的破坏。
|
||||
|
||||
# 许可证
|
||||
## 捐赠支持
|
||||
|
||||
- 工作不易,无意外情况此项目将继续维护下去,提供更多可能,欢迎打赏。<br/><br/>
|
||||
<img src="https://github.com/fankes/YuKiHookAPI/blob/master/img-src/wechat_code.jpg" width = "200" height = "200"/>
|
||||
|
||||
## 隐私政策
|
||||
|
||||
- [PRIVACY](https://github.com/fankes/ColorOSNotifyIcon/blob/master/PRIVACY.md)
|
||||
|
||||
## 许可证
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
@@ -61,5 +75,6 @@ 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/>.
|
||||
```
|
||||
|
||||
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)<br/><br/>
|
||||
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
|
||||
|
||||
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com)
|
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'com.google.devtools.ksp' version '1.6.10-1.0.2'
|
||||
id 'com.google.devtools.ksp' version '1.6.21-1.0.5'
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -25,65 +25,49 @@ android {
|
||||
versionCode rootProject.ext.appVersionCode
|
||||
versionName rootProject.ext.appVersionName
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled rootProject.ext.enableR8
|
||||
shrinkResources rootProject.ext.enableR8
|
||||
zipAlignEnabled rootProject.ext.enableR8
|
||||
signingConfig signingConfigs.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
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_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
|
||||
/** 移除无效耗时 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.67'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.67'
|
||||
implementation 'com.github.tiann:FreeReflection:3.1.0'
|
||||
implementation 'com.highcapable.yukihookapi:api:1.0.92'
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.92'
|
||||
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'
|
||||
implementation 'com.google.android.material:material:1.6.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
8
app/proguard-rules.pro
vendored
8
app/proguard-rules.pro
vendored
@@ -20,7 +20,6 @@
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontwarn
|
||||
-ignorewarnings
|
||||
-optimizationpasses 10
|
||||
-dontusemixedcaseclassnames
|
||||
@@ -35,13 +34,6 @@
|
||||
-renamesourcefileattribute P
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
-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
|
||||
|
||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||
public static *** throwUninitializedProperty(...);
|
||||
public static *** throwUninitializedPropertyAccessException(...);
|
||||
|
@@ -24,32 +24,13 @@
|
||||
|
||||
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.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)
|
||||
}
|
||||
|
@@ -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}_202203281333"
|
||||
|
||||
/** 当前模块的版本校验标签 */
|
||||
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"
|
||||
}
|
@@ -33,11 +33,16 @@ object DataConst {
|
||||
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_FORCE_APP_ICON = PrefsData("_notify_icon_force_app_icon", false)
|
||||
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 ENABLE_NOTIFY_PANEL_ALPHA = PrefsData("_enable_notify_panel_alpha", false)
|
||||
val ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP = PrefsData("_enable_notify_media_panel_auto_exp", false)
|
||||
val NOTIFY_ICON_CORNER = PrefsData("_notify_icon_corner", 10)
|
||||
val NOTIFY_PANEL_ALPHA = PrefsData("_notify_panel_alpha_pst", 75)
|
||||
val NOTIFY_ICON_DATAS = PrefsData("_notify_icon_datas", "")
|
||||
val NOTIFY_ICON_FIX_AUTO_TIME = PrefsData("_notify_icon_fix_auto_time", "07:00")
|
||||
|
||||
|
@@ -30,10 +30,10 @@ 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.xposed.proxy.IYukiHookXposedInit
|
||||
|
||||
@InjectYukiHookWithXposed
|
||||
class HookEntry : YukiHookXposedInitProxy {
|
||||
@InjectYukiHookWithXposed(isUsingResourcesHook = false)
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onInit() = configs {
|
||||
debugTag = "ColorOSNotify"
|
||||
@@ -41,13 +41,15 @@ class HookEntry : YukiHookXposedInitProxy {
|
||||
}
|
||||
|
||||
override fun onHook() = encase {
|
||||
loadApp(SYSTEMUI_PACKAGE_NAME) {
|
||||
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())
|
||||
else -> loadHooker(SystemUIHooker)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,15 +20,14 @@
|
||||
*
|
||||
* This file is Created by fankes on 2022/3/25.
|
||||
*/
|
||||
@file:Suppress("Recycle")
|
||||
@file:Suppress("StaticFieldLeak")
|
||||
|
||||
package com.fankes.coloros.notify.hook.entity
|
||||
|
||||
import android.app.WallpaperManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.Outline
|
||||
@@ -36,13 +35,16 @@ import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.os.SystemClock
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.util.ArrayMap
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.widget.ImageView
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.view.children
|
||||
import com.fankes.coloros.notify.bean.IconDataBean
|
||||
import com.fankes.coloros.notify.const.Const
|
||||
import com.fankes.coloros.notify.data.DataConst
|
||||
import com.fankes.coloros.notify.hook.HookConst.ANDROID_PACKAGE_NAME
|
||||
import com.fankes.coloros.notify.hook.HookConst.SYSTEMUI_PACKAGE_NAME
|
||||
@@ -52,9 +54,12 @@ import com.fankes.coloros.notify.param.IconPackParams
|
||||
import com.fankes.coloros.notify.utils.drawable.drawabletoolbox.DrawableBuilder
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import com.fankes.coloros.notify.utils.tool.IconAdaptationTool
|
||||
import com.fankes.coloros.notify.utils.tool.SystemUITool
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.field
|
||||
import com.highcapable.yukihookapi.hook.factory.hasMethod
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.log.loggerD
|
||||
import com.highcapable.yukihookapi.hook.log.loggerE
|
||||
@@ -68,41 +73,68 @@ import com.highcapable.yukihookapi.hook.type.java.LongType
|
||||
/**
|
||||
* 系统界面核心 Hook 类
|
||||
*/
|
||||
class SystemUIHooker : YukiBaseHooker() {
|
||||
|
||||
companion object {
|
||||
object SystemUIHooker : YukiBaseHooker() {
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val NotificationUtilsClass = "${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.NotificationUtils"
|
||||
private const val NotificationUtilsClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtils"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val NotificationEntryClass = "${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.collection.NotificationEntry"
|
||||
private const val NotificationEntryClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.collection.NotificationEntry"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val StatusBarIconClass = "com.android.internal.statusbar.StatusBarIcon"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val StatusBarIconViewClass = "${SYSTEMUI_PACKAGE_NAME}.statusbar.StatusBarIconView"
|
||||
private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val IconBuilderClass = "${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.icon.IconBuilder"
|
||||
private const val IconBuilderClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.icon.IconBuilder"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val IconManagerClass = "${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.icon.IconManager"
|
||||
private const val IconManagerClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.icon.IconManager"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val PluginManagerImplClass = "$SYSTEMUI_PACKAGE_NAME.shared.plugins.PluginManagerImpl"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val NotificationBackgroundViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.NotificationBackgroundView"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val PlayerViewHolderClass = "$SYSTEMUI_PACKAGE_NAME.media.PlayerViewHolder"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val MediaDataClass = "$SYSTEMUI_PACKAGE_NAME.media.MediaData"
|
||||
|
||||
/** ColorOS 存在的类 - 旧版本不存在 */
|
||||
private const val OplusContrastColorUtilClass = "com.oplusos.util.OplusContrastColorUtil"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val PluginManagerImplClass = "${SYSTEMUI_PACKAGE_NAME}.shared.plugins.PluginManagerImpl"
|
||||
/** ColorOS 存在的类 - 旧版本不存在 */
|
||||
private const val OplusNotificationBackgroundViewClass =
|
||||
"com.oplusos.systemui.statusbar.notification.row.OplusNotificationBackgroundView"
|
||||
|
||||
/** ColorOS 存在的类 - 旧版本不存在 */
|
||||
private const val OplusMediaControlPanelClass = "com.oplusos.systemui.media.OplusMediaControlPanel"
|
||||
|
||||
/** ColorOS 存在的类 - 旧版本不存在 */
|
||||
private const val OplusMediaViewControllerClass = "com.oplusos.systemui.media.OplusMediaViewController"
|
||||
|
||||
/** ColorOS 存在的类 - 旧版本不存在 */
|
||||
private const val BasePlayViewHolderClass = "com.oplusos.systemui.media.base.BasePlayViewHolder"
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val OplusNotificationIconAreaControllerClass = VariousClass(
|
||||
"com.oplusos.systemui.statusbar.phone.OplusNotificationIconAreaController",
|
||||
"com.oplusos.systemui.statusbar.policy.OplusNotificationIconAreaController",
|
||||
"com.coloros.systemui.statusbar.policy.ColorNotificationIconAreaController"
|
||||
)
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val SystemPromptControllerClass = VariousClass(
|
||||
"com.oplusos.systemui.statusbar.policy.SystemPromptController",
|
||||
"com.coloros.systemui.statusbar.policy.SystemPromptController"
|
||||
"com.coloros.systemui.statusbar.policy.ColorSystemPromptController"
|
||||
)
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
@@ -135,36 +167,47 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
"com.coloros.systemui.common.receiver.AbstractReceiver"
|
||||
)
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val StatusBarNotificationPresenterClass = VariousClass(
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.phone.StatusBarNotificationPresenter",
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.phone.StatusBar"
|
||||
)
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val ExpandableNotificationRowClass = VariousClass(
|
||||
"${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.row.ExpandableNotificationRow",
|
||||
"${SYSTEMUI_PACKAGE_NAME}.statusbar.ExpandableNotificationRow"
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.ExpandableNotificationRow",
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow"
|
||||
)
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val NotificationViewWrapperClass = VariousClass(
|
||||
"${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.row.wrapper.NotificationViewWrapper",
|
||||
"${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.NotificationViewWrapper"
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationViewWrapper",
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
|
||||
)
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val NotificationHeaderViewWrapperClass = VariousClass(
|
||||
"${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper",
|
||||
"${SYSTEMUI_PACKAGE_NAME}.statusbar.notification.NotificationHeaderViewWrapper"
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper",
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationHeaderViewWrapper"
|
||||
)
|
||||
}
|
||||
|
||||
/** 缓存的彩色 APP 图标 */
|
||||
private var appIcons = HashMap<String, Drawable>()
|
||||
private var appIcons = ArrayMap<String, Drawable>()
|
||||
|
||||
/** 缓存的通知优化图标数组 */
|
||||
private var iconDatas = ArrayList<IconDataBean>()
|
||||
|
||||
/** 缓存的状态栏小图标实例 */
|
||||
private var statusBarIconViews = HashSet<ImageView>()
|
||||
/** 状态栏通知图标容器 */
|
||||
private var notificationIconContainer: ViewGroup? = null
|
||||
|
||||
/** 缓存的通知小图标包装纸实例 */
|
||||
private var notificationViewWrappers = HashSet<Any>()
|
||||
/** 状态栏通知图标数组 */
|
||||
private var notificationIconInstances = ArrayList<View>()
|
||||
|
||||
/** 媒体通知 [View] */
|
||||
private var notificationPlayerView: View? = null
|
||||
|
||||
/** 通知栏通知控制器 */
|
||||
private var notificationPresenter: Any? = null
|
||||
|
||||
/** 仅监听一次主题壁纸颜色变化 */
|
||||
private var isWallpaperColorListenerSetUp = false
|
||||
@@ -172,65 +215,6 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
/** 是否已经使用过缓存刷新功能 */
|
||||
private var isUsingCachingMethod = false
|
||||
|
||||
/** 是否已经注册广播 */
|
||||
private var isRegisterReceiver = false
|
||||
|
||||
/** 用户解锁屏幕广播接收器 */
|
||||
private val userPresentReceiver by lazy {
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
/** 解锁后重新刷新状态栏图标防止系统重新设置它 */
|
||||
if (isUsingCachingMethod) refreshStatusBarIcons()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 模块广播接收器 */
|
||||
private val moduleCheckingReceiver by lazy {
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
context?.sendBroadcast(Intent().apply {
|
||||
action = Const.ACTION_MODULE_HANDLER_RECEIVER
|
||||
putExtra("isRegular", true)
|
||||
putExtra("isValied", intent?.isValiedModule)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 通知广播接收器 */
|
||||
private val remindCheckingReceiver by lazy {
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) = delayedRun(ms = 300) {
|
||||
if (intent?.isValiedModule == true)
|
||||
recachingPrefs(intent.getBooleanExtra("isRefreshCacheOnly", false))
|
||||
context?.sendBroadcast(Intent().apply {
|
||||
action = Const.ACTION_REMIND_HANDLER_RECEIVER
|
||||
putExtra("isGrasp", true)
|
||||
putExtra("isValied", intent?.isValiedModule)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断模块和宿主版本是否一致
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private val Intent.isValiedModule get() = getStringExtra(Const.MODULE_VERSION_VERIFY_TAG) == Const.MODULE_VERSION_VERIFY
|
||||
|
||||
/**
|
||||
* 注册广播接收器
|
||||
* @param context 实例
|
||||
*/
|
||||
private fun registerReceiver(context: Context) {
|
||||
if (isRegisterReceiver) return
|
||||
context.registerReceiver(userPresentReceiver, IntentFilter().apply { addAction(Intent.ACTION_USER_PRESENT) })
|
||||
context.registerReceiver(moduleCheckingReceiver, IntentFilter().apply { addAction(Const.ACTION_MODULE_CHECKING_RECEIVER) })
|
||||
context.registerReceiver(remindCheckingReceiver, IntentFilter().apply { addAction(Const.ACTION_REMIND_CHECKING_RECEIVER) })
|
||||
isRegisterReceiver = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用通知图标优化功能
|
||||
* @param isHooking 是否判断启用通知功能 - 默认:是
|
||||
@@ -245,6 +229,18 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
*/
|
||||
private val StatusBarNotification.isOplusPush get() = opPkg == ANDROID_PACKAGE_NAME && opPkg != packageName
|
||||
|
||||
/**
|
||||
* 判断通知背景是否为旧版本
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private val isOldNotificationBackground
|
||||
get() = safeOfFalse {
|
||||
NotificationBackgroundViewClass.clazz.hasMethod {
|
||||
name = "drawCustom"
|
||||
paramCount = 2
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印日志
|
||||
* @param tag 标识
|
||||
@@ -292,10 +288,10 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
.get(RoundRectDrawableUtilClass.clazz.field { name = "Companion" }.get().self)
|
||||
/** 启动一个线程防止卡顿 */
|
||||
Thread {
|
||||
statusBarIconViews.takeIf { it.isNotEmpty() }?.forEach {
|
||||
(notificationIconContainer?.children?.toList() ?: notificationIconInstances.takeIf { it.isNotEmpty() })?.forEach {
|
||||
runInSafe {
|
||||
/** 得到通知实例 */
|
||||
val nf = nfField.of<StatusBarNotification>(it) ?: return@Thread
|
||||
val nf = nfField.get(it).cast<StatusBarNotification>() ?: return@Thread
|
||||
|
||||
/** 得到原始通知图标 */
|
||||
val iconDrawable = nf.notification.smallIcon.loadDrawable(it.context)
|
||||
@@ -308,12 +304,12 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
drawable = iconDrawable
|
||||
).also { pair ->
|
||||
/** 得到图标圆角 */
|
||||
val sRadius = sRadiusField.ofFloat(it)
|
||||
val sRadius = sRadiusField.get(it).float()
|
||||
|
||||
/** 得到缩放大小 */
|
||||
val sNfSize = sNfSizeField.ofInt(it)
|
||||
val sNfSize = sNfSizeField.get(it).int()
|
||||
/** 在主线程设置图标 */
|
||||
it.post { it.setImageDrawable(roundUtil.invoke(pair.first, sRadius, sNfSize, sNfSize, it.context)) }
|
||||
it.post { (it as? ImageView?)?.setImageDrawable(roundUtil.invoke(pair.first, sRadius, sNfSize, sNfSize, it.context)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,9 +318,13 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
|
||||
/** 刷新通知小图标 */
|
||||
private fun refreshNotificationIcons() = runInSafe {
|
||||
NotificationHeaderViewWrapperClass.clazz.method { name = "resolveHeaderViews" }.also { result ->
|
||||
notificationViewWrappers.takeIf { it.isNotEmpty() }?.forEach { result.get(it).call() }
|
||||
notificationPresenter?.current {
|
||||
method {
|
||||
name = "updateNotificationsOnDensityOrFontScaleChanged"
|
||||
emptyParam()
|
||||
}.call()
|
||||
}
|
||||
modifyNotifyPanelAlpha(notificationPlayerView, isTint = true)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,7 +343,7 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
}.get(it.method {
|
||||
name = "getInstance"
|
||||
param(ContextClass)
|
||||
}.get().invoke(context)).callBoolean(drawable)
|
||||
}.get().invoke(context)).boolean(drawable)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,6 +428,13 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
) = runInSafe {
|
||||
compatCustomIcon(isGrayscaleIcon, packageName).also { customPair ->
|
||||
when {
|
||||
prefs.get(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON) && isEnableHookColorNotifyIcon(isHooking = false) ->
|
||||
iconView.apply {
|
||||
/** 重新设置图标 */
|
||||
setImageDrawable(appIcons[packageName] ?: context.findAppIcon(packageName))
|
||||
/** 设置默认样式 */
|
||||
setDefaultNotifyIconViewStyle()
|
||||
}
|
||||
customPair.first != null || isGrayscaleIcon -> iconView.apply {
|
||||
/** 设置不要裁切到边界 */
|
||||
clipToOutline = false
|
||||
@@ -455,7 +462,12 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
|
||||
/** 判断风格并开始 Hook */
|
||||
if (isA12Style) {
|
||||
background = DrawableBuilder().rounded().solidColor(newApplyColor).build()
|
||||
/** 通知图标边框圆角大小 */
|
||||
background = DrawableBuilder()
|
||||
.rectangle()
|
||||
.cornerRadius(prefs.get(DataConst.NOTIFY_ICON_CORNER).dp(context))
|
||||
.solidColor(newApplyColor)
|
||||
.build()
|
||||
setColorFilter(newStyle)
|
||||
setPadding(2.dp(context), 2.dp(context), 2.dp(context), 2.dp(context))
|
||||
} else {
|
||||
@@ -467,6 +479,46 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
else -> iconView.apply {
|
||||
/** 重新设置图标 */
|
||||
setImageDrawable(nf.compatPushingIcon(drawable))
|
||||
/** 设置默认样式 */
|
||||
setDefaultNotifyIconViewStyle()
|
||||
}
|
||||
}
|
||||
/** 打印日志 */
|
||||
printLogcat(tag = "NotifyIcon", iconView.context, packageName, isCustom = customPair.first != null, isGrayscaleIcon)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置通知面板背景透明度
|
||||
* @param view 背景 View 实例
|
||||
* @param drawable 背景实例
|
||||
* @param isTint 是否着色 [view]
|
||||
*/
|
||||
private fun modifyNotifyPanelAlpha(view: View?, drawable: Drawable? = null, isTint: Boolean = false) {
|
||||
prefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA).also { isEnabled ->
|
||||
val currentAlpha = prefs.get(DataConst.NOTIFY_PANEL_ALPHA)
|
||||
val currendColor = if (view?.context?.isSystemInDarkMode == true) 0xFF404040.toInt() else 0xFFFAFAFA.toInt()
|
||||
/** 设置通知面板背景透明度 */
|
||||
when {
|
||||
isEnabled.not() -> {
|
||||
if (isTint) view?.backgroundTintList = ColorStateList.valueOf(currendColor)
|
||||
else drawable?.setTint(currendColor)
|
||||
}
|
||||
isTint.not() && view?.parent?.parent?.javaClass?.name?.contains(other = "ChildrenContainer") == true -> drawable?.alpha = 0
|
||||
else -> {
|
||||
currendColor.colorAlphaOf(currentAlpha / 100f).also {
|
||||
if (isTint) view?.backgroundTintList = ColorStateList.valueOf(it)
|
||||
else drawable?.setTint(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 移除阴影效果 */
|
||||
if (isEnabled) view?.elevation = 0f
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置默认通知栏通知图标样式 */
|
||||
private fun ImageView.setDefaultNotifyIconViewStyle() {
|
||||
/** 设置裁切到边界 */
|
||||
clipToOutline = true
|
||||
/** 设置一个圆角轮廓裁切 */
|
||||
@@ -485,10 +537,13 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
/** 清除着色 */
|
||||
colorFilter = null
|
||||
}
|
||||
}
|
||||
/** 打印日志 */
|
||||
printLogcat(tag = "NotifyIcon", iconView.context, packageName, isCustom = customPair.first != null, isGrayscaleIcon)
|
||||
}
|
||||
|
||||
/** 注册 */
|
||||
private fun register() {
|
||||
/** 解锁后重新刷新状态栏图标防止系统重新设置它 */
|
||||
onAppLifecycle { registerReceiver(Intent.ACTION_USER_PRESENT) { _, _ -> if (isUsingCachingMethod) refreshStatusBarIcons() } }
|
||||
/** 刷新图标缓存 */
|
||||
SystemUITool.Host.onRefreshSystemUI(param = this) { recachingPrefs(it) }
|
||||
}
|
||||
|
||||
/** 缓存图标数据 */
|
||||
@@ -505,17 +560,25 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
/**
|
||||
* 刷新缓存数据
|
||||
* @param isRefreshCacheOnly 仅刷新缓存不刷新图标和通知改变 - 默认:否
|
||||
* @return [Boolean] 是否成功
|
||||
*/
|
||||
private fun recachingPrefs(isRefreshCacheOnly: Boolean = false) {
|
||||
private fun recachingPrefs(isRefreshCacheOnly: Boolean = false): Boolean {
|
||||
/** 必要的延迟防止 Sp 存储不刷新 */
|
||||
SystemClock.sleep(100)
|
||||
/** 获取可读写状态 */
|
||||
return prefs.isXSharePrefsReadable.also {
|
||||
isUsingCachingMethod = true
|
||||
prefs.clearCache()
|
||||
cachingIconDatas()
|
||||
if (isRefreshCacheOnly) return
|
||||
if (isRefreshCacheOnly) return@also
|
||||
refreshStatusBarIcons()
|
||||
refreshNotificationIcons()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
/** 注册 */
|
||||
register()
|
||||
/** 缓存图标数据 */
|
||||
cachingIconDatas()
|
||||
/** 移除开发者警告通知 */
|
||||
@@ -537,7 +600,7 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
}
|
||||
beforeHook {
|
||||
/** 是否移除 */
|
||||
if (args().ofInt() == 7 && prefs.get(DataConst.REMOVE_CHANGECP_NOTIFY)) resultNull()
|
||||
if (args().first().int() == 7 && prefs.get(DataConst.REMOVE_CHANGECP_NOTIFY)) resultNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -559,9 +622,9 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
injectMember {
|
||||
method {
|
||||
name = "isGrayscaleOplus"
|
||||
param(ImageViewClass, OplusContrastColorUtilClass.clazz)
|
||||
param(ImageViewClass, OplusContrastColorUtilClass)
|
||||
}
|
||||
replaceAny { firstArgs<ImageView>()?.let { isGrayscaleIcon(it.context, it.drawable) } }
|
||||
replaceAny { args().first().cast<ImageView>()?.let { isGrayscaleIcon(it.context, it.drawable) } }
|
||||
}.ignoredHookingFailure()
|
||||
}
|
||||
/** 替换状态栏图标 */
|
||||
@@ -569,14 +632,14 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
injectMember {
|
||||
method {
|
||||
name = "getIconDescriptor"
|
||||
param(NotificationEntryClass.clazz, BooleanType)
|
||||
param(NotificationEntryClass, BooleanType)
|
||||
}
|
||||
afterHook {
|
||||
IconBuilderClass.clazz.field { name = "context" }
|
||||
.of<Context>(field { name = "iconBuilder" }.of(instance))?.also { context ->
|
||||
.get(field { name = "iconBuilder" }.get(instance).cast()).cast<Context>()?.also { context ->
|
||||
NotificationEntryClass.clazz.method {
|
||||
name = "getSbn"
|
||||
}.get(firstArgs).invoke<StatusBarNotification>()?.also { nf ->
|
||||
}.get(args().first().any()).invoke<StatusBarNotification>()?.also { nf ->
|
||||
nf.notification.smallIcon.loadDrawable(context).also { iconDrawable ->
|
||||
compatStatusIcon(
|
||||
context = context,
|
||||
@@ -608,21 +671,141 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
param(StatusBarNotificationClass)
|
||||
}
|
||||
afterHook {
|
||||
if (firstArgs != null) instance<ImageView>().also {
|
||||
registerWallpaperColorChanged(it)
|
||||
registerReceiver(it.context)
|
||||
statusBarIconViews.add(it)
|
||||
/** 注册壁纸颜色监听 */
|
||||
if (args().first().any() != null) instance<ImageView>().also { registerWallpaperColorChanged(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 注入通知控制器实例 */
|
||||
StatusBarNotificationPresenterClass.hook {
|
||||
injectMember {
|
||||
allConstructors()
|
||||
afterHook { notificationPresenter = instance }
|
||||
}
|
||||
}
|
||||
/** 注入状态栏通知图标容器实例 */
|
||||
OplusNotificationIconAreaControllerClass.hook {
|
||||
injectMember {
|
||||
var isOldWay = false
|
||||
method {
|
||||
name = "updateIconsForLayout"
|
||||
paramCount = 10
|
||||
}.remedys {
|
||||
method {
|
||||
name = "updateIconsForLayout"
|
||||
paramCount = 1
|
||||
}.onFind { isOldWay = true }
|
||||
}
|
||||
afterHook {
|
||||
if (isOldWay) {
|
||||
notificationIconInstances.clear()
|
||||
field { name = "mLastToShow" }.get(instance).list<View>()
|
||||
.takeIf { it.isNotEmpty() }?.forEach { notificationIconInstances.add(it) }
|
||||
} else notificationIconContainer = args(index = 1).cast()
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 替换通知面板背景 - 新版本 */
|
||||
OplusNotificationBackgroundViewClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "drawRegionBlur"
|
||||
paramCount = 2
|
||||
}.remedys {
|
||||
method {
|
||||
name = "draw"
|
||||
paramCount = 2
|
||||
}
|
||||
}
|
||||
beforeHook { modifyNotifyPanelAlpha(instance(), args().last().cast<Drawable>()) }
|
||||
}
|
||||
injectMember {
|
||||
method {
|
||||
name = "draw"
|
||||
paramCount = 2
|
||||
superClass(isOnlySuperClass = true)
|
||||
}
|
||||
beforeHook { modifyNotifyPanelAlpha(instance(), args().last().cast<Drawable>()) }
|
||||
}
|
||||
}.by { isOldNotificationBackground.not() }
|
||||
/** 替换通知面板背景 - 旧版本 */
|
||||
NotificationBackgroundViewClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "draw"
|
||||
paramCount = 2
|
||||
}
|
||||
beforeHook { modifyNotifyPanelAlpha(instance(), args().last().cast<Drawable>()) }
|
||||
}
|
||||
injectMember {
|
||||
method {
|
||||
name = "drawCustom"
|
||||
paramCount = 2
|
||||
}
|
||||
beforeHook { modifyNotifyPanelAlpha(instance(), args().last().cast<Drawable>()) }
|
||||
}
|
||||
}.by { isOldNotificationBackground }
|
||||
/** 替换通知面板背景 - 避免折叠展开通知二次修改通知面板背景 */
|
||||
ExpandableNotificationRowClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "updateBackgroundForGroupState"
|
||||
emptyParam()
|
||||
}
|
||||
beforeHook {
|
||||
if (prefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA))
|
||||
field { name = "mShowGroupBackgroundWhenExpanded" }.get(instance).setTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 替换媒体通知面板背景 - 设置媒体通知自动展开 */
|
||||
OplusMediaControlPanelClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "bind"
|
||||
paramCount = 2
|
||||
}
|
||||
afterHook {
|
||||
/** 得到当前实例 */
|
||||
val holder = field {
|
||||
name = "mViewHolder"
|
||||
superClass(isOnlySuperClass = true)
|
||||
}.get(instance).any()
|
||||
/** 记录媒体通知 [View] */
|
||||
notificationPlayerView = PlayerViewHolderClass.clazz.method {
|
||||
name = "getPlayer"
|
||||
emptyParam()
|
||||
}.get(holder).invoke()
|
||||
/** 设置背景着色 */
|
||||
modifyNotifyPanelAlpha(notificationPlayerView, isTint = true)
|
||||
/** 当前是否正在播放 */
|
||||
val isPlaying = MediaDataClass.clazz.method {
|
||||
name = "isPlaying"
|
||||
emptyParam()
|
||||
}.get(args().first().any()).boolean()
|
||||
|
||||
/** 当前通知是否展开 */
|
||||
val isExpanded = OplusMediaViewControllerClass.clazz.method {
|
||||
name = "getExpanded"
|
||||
emptyParam()
|
||||
}.get(field { name = "mOplusMediaViewController" }.get(instance).self).boolean()
|
||||
/** 符合条件后执行 */
|
||||
if (prefs.get(DataConst.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP).not() || isExpanded || isPlaying.not()) return@afterHook
|
||||
/** 模拟手动展开通知 */
|
||||
BasePlayViewHolderClass.clazz.method {
|
||||
name = "getExpandButton"
|
||||
emptyParam()
|
||||
}.get(holder).invoke<View>()?.performClick()
|
||||
}
|
||||
}
|
||||
}.ignoredHookClassNotFoundFailure()
|
||||
/** 替换通知图标和样式 */
|
||||
NotificationHeaderViewWrapperClass.hook {
|
||||
injectMember {
|
||||
method { name = "resolveHeaderViews" }
|
||||
afterHook {
|
||||
NotificationHeaderViewWrapperClass.clazz
|
||||
.field { name = "mIcon" }.of<ImageView>(instance)?.apply {
|
||||
.field { name = "mIcon" }.get(instance).cast<ImageView>()?.apply {
|
||||
ExpandableNotificationRowClass.clazz
|
||||
.method { name = "getEntry" }
|
||||
.get(NotificationViewWrapperClass.clazz.field {
|
||||
@@ -649,11 +832,6 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 记录实例 */
|
||||
injectMember {
|
||||
constructor { param(ContextClass, ViewClass, ExpandableNotificationRowClass.clazz) }
|
||||
afterHook { notificationViewWrappers.add(instance) }
|
||||
}
|
||||
}
|
||||
/** 发送适配新的 APP 图标通知 */
|
||||
PluginManagerImplClass.hook {
|
||||
@@ -663,7 +841,7 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
param(ContextClass, IntentClass)
|
||||
}
|
||||
afterHook {
|
||||
if (isEnableHookColorNotifyIcon()) (lastArgs as? Intent)?.also {
|
||||
if (isEnableHookColorNotifyIcon()) args().last().cast<Intent>()?.also {
|
||||
if (it.action.equals(Intent.ACTION_PACKAGE_REPLACED).not() &&
|
||||
it.getBooleanExtra(Intent.EXTRA_REPLACING, false)
|
||||
) return@also
|
||||
@@ -673,11 +851,11 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
if (iconDatas.takeIf { e -> e.isNotEmpty() }
|
||||
?.filter { e -> e.packageName == newPkgName }
|
||||
.isNullOrEmpty()
|
||||
) IconAdaptationTool.pushNewAppSupportNotify(firstArgs()!!, newPkgName)
|
||||
) IconAdaptationTool.pushNewAppSupportNotify(args().first().cast()!!, newPkgName)
|
||||
}
|
||||
Intent.ACTION_PACKAGE_REMOVED ->
|
||||
IconAdaptationTool.removeNewAppSupportNotify(
|
||||
context = firstArgs()!!,
|
||||
context = args().first().cast()!!,
|
||||
packageName = it.data?.schemeSpecificPart ?: ""
|
||||
)
|
||||
}
|
||||
@@ -693,9 +871,11 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
param(ContextClass, IntentClass)
|
||||
}
|
||||
afterHook {
|
||||
args().first().cast<Context>()?.also {
|
||||
/** 注册定时监听 */
|
||||
if (isEnableHookColorNotifyIcon() && prefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_AUTO))
|
||||
IconAdaptationTool.prepareAutoUpdateIconRule(
|
||||
context = firstArgs()!!,
|
||||
context = it,
|
||||
timeSet = prefs.get(DataConst.NOTIFY_ICON_FIX_AUTO_TIME)
|
||||
)
|
||||
}
|
||||
@@ -703,3 +883,4 @@ class SystemUIHooker : YukiBaseHooker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -43,7 +43,7 @@ import com.fankes.coloros.notify.ui.activity.base.BaseActivity
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
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>() {
|
||||
|
||||
@@ -61,7 +61,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
|
||||
override fun onCreate() {
|
||||
/** 检查激活状态 */
|
||||
if (isXposedModuleActive.not()) {
|
||||
if (YukiHookAPI.Status.isXposedModuleActive.not()) {
|
||||
showDialog {
|
||||
title = "模块没有激活"
|
||||
msg = "模块没有激活,你无法使用这里的功能,请先激活模块。"
|
||||
@@ -154,9 +154,20 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
holder.adpAppAllSwitch.isChecked = isAppNotifyHookAllOf(it)
|
||||
holder.adpAppAllSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
fun saveState() {
|
||||
putAppNotifyHookAllOf(it, 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!!
|
||||
}
|
||||
@@ -176,7 +187,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(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md") }
|
||||
cancelButton(text = "请求适配") { openBrowser(url = "https://github.com/fankes/ColorOSNotifyIcon/issues/new/choose") }
|
||||
neutralButton(text = "暂时不用")
|
||||
}
|
||||
}
|
||||
/** 装载数据 */
|
||||
mockLocalData()
|
||||
|
@@ -26,6 +26,8 @@ package com.fankes.coloros.notify.ui.activity
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.pm.PackageManager
|
||||
import android.widget.SeekBar
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.coloros.notify.BuildConfig
|
||||
import com.fankes.coloros.notify.R
|
||||
@@ -36,9 +38,9 @@ import com.fankes.coloros.notify.ui.activity.base.BaseActivity
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
import com.fankes.coloros.notify.utils.tool.GithubReleaseTool
|
||||
import com.fankes.coloros.notify.utils.tool.SystemUITool
|
||||
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
|
||||
import com.fankes.coloros.notify.utils.tool.YukiPromoteTool
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
|
||||
|
||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
@@ -85,7 +87,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
noCancelable()
|
||||
}
|
||||
/** 判断是否 Hook */
|
||||
isXposedModuleActive -> {
|
||||
YukiHookAPI.Status.isXposedModuleActive -> {
|
||||
if (IconPackParams(context = this).iconDatas.isEmpty() && modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX))
|
||||
showDialog {
|
||||
title = "配置通知图标优化名单"
|
||||
@@ -103,6 +105,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
cancelButton()
|
||||
noCancelable()
|
||||
}
|
||||
/** 推广、恰饭 */
|
||||
YukiPromoteTool.promote(context = this)
|
||||
}
|
||||
else ->
|
||||
showDialog {
|
||||
@@ -120,20 +124,38 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
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.notifyIconCustomCornerItem.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX) &&
|
||||
modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON).not()
|
||||
binding.notifyIconForceAppIconItem.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.notifyPanelConfigSeekbar.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA)
|
||||
binding.notifyPanelConfigTextPanel.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA)
|
||||
binding.notifyPanelConfigWarnPanel.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA)
|
||||
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.notifyMediaPanelAutoExpSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP)
|
||||
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.notifyIconForceAppIconSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON)
|
||||
binding.notifyIconFixNotifySwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY)
|
||||
binding.notifyIconAutoSyncSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_AUTO)
|
||||
binding.notifyPanelConfigSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA)
|
||||
binding.notifyPanelConfigSeekbar.progress = modulePrefs.get(DataConst.NOTIFY_PANEL_ALPHA)
|
||||
binding.notifyIconCustomCornerSeekbar.progress = modulePrefs.get(DataConst.NOTIFY_ICON_CORNER)
|
||||
binding.notifyPanelConfigText.text = "${modulePrefs.get(DataConst.NOTIFY_PANEL_ALPHA)}%"
|
||||
binding.notifyIconCustomCornerText.text = "${modulePrefs.get(DataConst.NOTIFY_ICON_CORNER)} dp"
|
||||
binding.notifyIconAutoSyncText.text = notifyIconAutoSyncTime
|
||||
/** 媒体通知自动展开仅支持 12.1 - 旧版本适配过于复杂已放弃 */
|
||||
if (colorOSNumberVersion != "V12.1") {
|
||||
binding.notifyMediaPanelAutoExpSwitch.isVisible = false
|
||||
binding.notifyMediaPanelAutoExpText.isVisible = false
|
||||
}
|
||||
binding.moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_MODULE, b)
|
||||
@@ -161,10 +183,29 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX, b)
|
||||
binding.notifyIconFixButton.isVisible = b
|
||||
binding.notifyIconCustomCornerItem.isVisible = b && modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON).not()
|
||||
binding.notifyIconForceAppIconItem.isVisible = b
|
||||
binding.notifyIconFixNotifyItem.isVisible = b
|
||||
binding.notifyIconAutoSyncItem.isVisible = b
|
||||
SystemUITool.refreshSystemUI(context = this)
|
||||
}
|
||||
binding.notifyIconForceAppIconSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
fun saveState() {
|
||||
binding.notifyIconCustomCornerItem.isVisible = b.not()
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON, b)
|
||||
SystemUITool.refreshSystemUI(context = this)
|
||||
}
|
||||
if (b) showDialog {
|
||||
title = "破坏性功能警告"
|
||||
msg = "开启这个功能后,任何通知栏中的通知图标都会被强制替换为当前推送通知的 APP 的图标," +
|
||||
"某些系统级别的 APP 通知图标可能会显示异常或发生图标丢失。\n\n" +
|
||||
"此功能仅面向一些追求图标美观度的用户,我们不推荐开启这个功能,且发生任何 BUG 都不会去修复,仍然继续开启吗?"
|
||||
confirmButton { saveState() }
|
||||
cancelButton { btn.isChecked = btn.isChecked.not() }
|
||||
noCancelable()
|
||||
} else saveState()
|
||||
}
|
||||
binding.notifyIconFixNotifySwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY, b)
|
||||
@@ -190,12 +231,49 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
modulePrefs.put(DataConst.ENABLE_ANDROID12_STYLE, b)
|
||||
SystemUITool.refreshSystemUI(context = this)
|
||||
}
|
||||
binding.notifyMediaPanelAutoExpSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP, b)
|
||||
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
|
||||
}
|
||||
binding.notifyPanelConfigSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
modulePrefs.put(DataConst.ENABLE_NOTIFY_PANEL_ALPHA, b)
|
||||
binding.notifyPanelConfigTextPanel.isVisible = b
|
||||
binding.notifyPanelConfigWarnPanel.isVisible = b
|
||||
binding.notifyPanelConfigSeekbar.isVisible = b
|
||||
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.notifyPanelConfigSeekbar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
binding.notifyPanelConfigText.text = "$progress%"
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
modulePrefs.put(DataConst.NOTIFY_PANEL_ALPHA, seekBar.progress)
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
binding.notifyIconCustomCornerSeekbar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
binding.notifyIconCustomCornerText.text = "$progress dp"
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
modulePrefs.put(DataConst.NOTIFY_ICON_CORNER, seekBar.progress)
|
||||
SystemUITool.refreshSystemUI(context = this@MainActivity)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
/** 通知图标优化名单按钮点击事件 */
|
||||
binding.notifyIconFixButton.setOnClickListener { navigate<ConfigureActivity>() }
|
||||
/** 自动更新在线规则修改时间按钮点击事件 */
|
||||
@@ -234,36 +312,36 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
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()) -> 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
|
||||
YukiHookAPI.Status.isXposedModuleActive -> R.mipmap.ic_success
|
||||
else -> R.mipmap.ic_warn
|
||||
}
|
||||
)
|
||||
binding.mainTextStatus.text =
|
||||
when {
|
||||
isXposedModuleActive && isModuleRegular.not() && modulePrefs.get(DataConst.ENABLE_MODULE).not() -> "模块已停用"
|
||||
isXposedModuleActive && isModuleRegular.not() -> "模块已激活,请重启系统界面"
|
||||
isXposedModuleActive && isModuleValied.not() -> "模块已更新,请重启系统界面"
|
||||
isXposedModuleActive -> "模块已激活"
|
||||
YukiHookAPI.Status.isXposedModuleActive && isModuleRegular.not() && modulePrefs.get(DataConst.ENABLE_MODULE).not() -> "模块已停用"
|
||||
YukiHookAPI.Status.isXposedModuleActive && isModuleRegular.not() -> "模块已激活,请重启系统界面"
|
||||
YukiHookAPI.Status.isXposedModuleActive && isModuleValied.not() -> "模块已更新,请重启系统界面"
|
||||
YukiHookAPI.Status.isXposedModuleActive -> "模块已激活"
|
||||
else -> "模块未激活"
|
||||
}
|
||||
binding.mainTextApiWay.isVisible = isXposedModuleActive
|
||||
binding.mainTextApiWay.text = "Activated by ${YukiHookModuleStatus.executorName} API ${YukiHookModuleStatus.executorVersion}"
|
||||
binding.mainTextApiWay.isVisible = YukiHookAPI.Status.isXposedModuleActive
|
||||
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.executorName} API ${YukiHookAPI.Status.executorVersion}"
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
/** 刷新模块状态 */
|
||||
refreshModuleStatus()
|
||||
/** 发送广播检查模块激活状态 */
|
||||
SystemUITool.checkingActivated(context = this) { isRegular, isValied ->
|
||||
isModuleRegular = isRegular
|
||||
/** 检查模块激活状态 */
|
||||
SystemUITool.checkingActivated(context = this) { isValied ->
|
||||
isModuleRegular = true
|
||||
isModuleValied = isValied
|
||||
refreshModuleStatus()
|
||||
}
|
||||
|
@@ -28,11 +28,11 @@ 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
|
||||
import com.fankes.coloros.notify.utils.tool.SystemUITool
|
||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
|
||||
|
||||
class NotifyIconRuleUpdateActivity : Activity() {
|
||||
|
||||
@@ -48,8 +48,6 @@ class NotifyIconRuleUpdateActivity : Activity() {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
/** 注册 */
|
||||
SystemUITool.register(context = this)
|
||||
/** 拉取云端数据 */
|
||||
IconRuleManagerTool.sync(appContext) {
|
||||
/** 刷新系统界面 */
|
||||
@@ -60,10 +58,4 @@ class NotifyIconRuleUpdateActivity : Activity() {
|
||||
/** 切换到后台 */
|
||||
moveTaskToBack(true)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
/** 解除注册 */
|
||||
SystemUITool.unregister(context = this)
|
||||
}
|
||||
}
|
@@ -26,11 +26,11 @@ package com.fankes.coloros.notify.ui.activity.base
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
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
|
||||
@@ -61,26 +61,19 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
/** 隐藏系统的标题栏 */
|
||||
supportActionBar?.hide()
|
||||
/** 初始化沉浸状态栏 */
|
||||
immersionBar {
|
||||
statusBarColor(R.color.colorThemeBackground)
|
||||
autoDarkModeEnable(true)
|
||||
statusBarDarkFont(isNotSystemInDarkMode)
|
||||
navigationBarColor(R.color.colorThemeBackground)
|
||||
navigationBarDarkIcon(isNotSystemInDarkMode)
|
||||
fitsSystemWindows(true)
|
||||
ViewCompat.getWindowInsetsController(window.decorView)?.apply {
|
||||
isAppearanceLightStatusBars = isNotSystemInDarkMode
|
||||
isAppearanceLightNavigationBars = isNotSystemInDarkMode
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
@@ -39,25 +39,25 @@ import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.highcapable.yukihookapi.annotation.DoNotUseField
|
||||
import com.highcapable.yukihookapi.annotation.CauseProblemsApi
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
|
||||
/**
|
||||
* 构造对话框
|
||||
* @param isUseBlackTheme 是否使用深色主题
|
||||
* @param it 对话框方法体
|
||||
* @param initiate 对话框方法体
|
||||
*/
|
||||
fun Context.showDialog(isUseBlackTheme: Boolean = false, it: DialogBuilder.() -> Unit) =
|
||||
DialogBuilder(this, isUseBlackTheme).apply(it).show()
|
||||
fun Context.showDialog(isUseBlackTheme: Boolean = false, initiate: DialogBuilder.() -> Unit) =
|
||||
DialogBuilder(context = this, isUseBlackTheme).apply(initiate).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()
|
||||
|
||||
/**
|
||||
* 对话框构造器
|
||||
@@ -71,7 +71,7 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
|
||||
|
||||
private var dialogInstance: Dialog? = null // 对话框实例
|
||||
|
||||
@DoNotUseField
|
||||
@CauseProblemsApi
|
||||
var customLayoutView: View? = null // 自定义布局
|
||||
|
||||
/**
|
||||
@@ -150,34 +150,34 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
|
||||
/**
|
||||
* 设置对话框确定按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun confirmButton(text: String = "确定", it: () -> Unit = {}) {
|
||||
fun confirmButton(text: String = "确定", callback: () -> Unit = {}) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setPositiveButton(text) { _, _ -> it() } }
|
||||
else runInSafe { instanceAndroid?.setPositiveButton(text) { _, _ -> it() } }
|
||||
runInSafe { instanceAndroidX?.setPositiveButton(text) { _, _ -> callback() } }
|
||||
else runInSafe { instanceAndroid?.setPositiveButton(text) { _, _ -> callback() } }
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框取消按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun cancelButton(text: String = "取消", it: () -> Unit = {}) {
|
||||
fun cancelButton(text: String = "取消", callback: () -> Unit = {}) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setNegativeButton(text) { _, _ -> it() } }
|
||||
else runInSafe { instanceAndroid?.setNegativeButton(text) { _, _ -> it() } }
|
||||
runInSafe { instanceAndroidX?.setNegativeButton(text) { _, _ -> callback() } }
|
||||
else runInSafe { instanceAndroid?.setNegativeButton(text) { _, _ -> callback() } }
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框第三个按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param it 点击事件
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun neutralButton(text: String = "更多", it: () -> Unit = {}) {
|
||||
fun neutralButton(text: String = "更多", callback: () -> Unit = {}) {
|
||||
if (isUsingAndroidX)
|
||||
runInSafe { instanceAndroidX?.setNeutralButton(text) { _, _ -> it() } }
|
||||
else runInSafe { instanceAndroid?.setNeutralButton(text) { _, _ -> it() } }
|
||||
runInSafe { instanceAndroidX?.setNeutralButton(text) { _, _ -> callback() } }
|
||||
else runInSafe { instanceAndroid?.setNeutralButton(text) { _, _ -> callback() } }
|
||||
}
|
||||
|
||||
/** 取消对话框 */
|
||||
|
@@ -33,6 +33,7 @@ import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
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.res.Configuration
|
||||
@@ -49,13 +50,13 @@ import android.util.Base64
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import com.fankes.coloros.notify.application.CNNApplication.Companion.appContext
|
||||
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.xposed.application.ModuleApplication.Companion.appContext
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -137,14 +138,20 @@ val colorOSFullVersion get() = "${if (isRealmeUI) "RealmeUI " else ""}$colorOSVe
|
||||
* 获取 ColorOS 版本
|
||||
* @return [String]
|
||||
*/
|
||||
val colorOSVersion
|
||||
val colorOSVersion get() = "$colorOSNumberVersion ${Build.DISPLAY}"
|
||||
|
||||
/**
|
||||
* 获取 ColorOS 数字版本
|
||||
* @return [String]
|
||||
*/
|
||||
val colorOSNumberVersion
|
||||
get() = safeOf(default = "无法获取") {
|
||||
(classOf(name = "com.oplus.os.OplusBuild").let {
|
||||
it.field { name = "VERSIONS" }.ignoredError().of<Array<String>>()
|
||||
?.get(it.method { name = "getOplusOSVERSION" }.ignoredError().get().callInt() - 1)
|
||||
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}"
|
||||
).split("ssi:")[1].split("/")[0].trim())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,6 +200,14 @@ fun Context.findAppName(name: String) =
|
||||
fun Context.findAppIcon(name: String) =
|
||||
safeOfNull { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadIcon(packageManager) }
|
||||
|
||||
/**
|
||||
* 获取 APP 是否为 DEBUG 版本
|
||||
* @param name APP 包名
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun Context.isAppDebuggable(name: String) =
|
||||
safeOfFalse { (packageManager?.getPackageInfo(name, 0)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 }
|
||||
|
||||
/**
|
||||
* 对数值自动补零
|
||||
* @return [String]
|
||||
@@ -271,6 +286,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]
|
||||
@@ -320,12 +342,19 @@ fun findPropString(key: String, default: String = "") = safeOf(default) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行命令 - su
|
||||
* 是否有 Root 权限
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val isRootAccess get() = safeOfFalse { 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 {
|
||||
(if (isSu) Shell.su(cmd) else Shell.sh(cmd)).exec().out.let {
|
||||
if (it.isNotEmpty()) it[0].trim() else ""
|
||||
}
|
||||
}
|
||||
@@ -341,27 +370,23 @@ fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).sho
|
||||
*
|
||||
* [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()
|
||||
|
||||
/**
|
||||
|
@@ -45,9 +45,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")
|
||||
@@ -73,7 +73,7 @@ object GithubReleaseTool {
|
||||
}
|
||||
if (name != version) (context as? Activity?)?.runOnUiThread {
|
||||
showUpdate()
|
||||
it(name) { showUpdate() }
|
||||
result(name) { showUpdate() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,9 +84,9 @@ object GithubReleaseTool {
|
||||
/**
|
||||
* 检查网络连接情况
|
||||
* @param context 实例
|
||||
* @param it 已连接回调
|
||||
* @param result 已连接回调
|
||||
*/
|
||||
private fun checkingInternetConnect(context: Context, it: () -> Unit) = runInSafe {
|
||||
private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe {
|
||||
if (isNetWorkSuccess)
|
||||
OkHttpClient().newBuilder().build().newCall(
|
||||
Request.Builder()
|
||||
@@ -107,7 +107,7 @@ object GithubReleaseTool {
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) = runInSafe {
|
||||
(context as? Activity?)?.runOnUiThread { runInSafe { it() } }
|
||||
(context as? Activity?)?.runOnUiThread { runInSafe { result() } }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ 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.BuildConfig
|
||||
import com.fankes.coloros.notify.hook.HookEntry
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
|
||||
@@ -44,6 +44,9 @@ import com.fankes.coloros.notify.utils.factory.*
|
||||
*/
|
||||
object IconAdaptationTool {
|
||||
|
||||
/** 当前模块的包名 */
|
||||
private const val MODULE_PACKAGE_NAME = BuildConfig.APPLICATION_ID
|
||||
|
||||
/** 推送通知的渠道名称 */
|
||||
private const val NOTIFY_CHANNEL = "notifyRuleSupportId"
|
||||
|
||||
@@ -116,6 +119,7 @@ object IconAdaptationTool {
|
||||
* @param packageName 安装的 APP 包名
|
||||
*/
|
||||
fun pushNewAppSupportNotify(context: Context, packageName: String) {
|
||||
if (context.isAppDebuggable(packageName)) return
|
||||
context.getSystemService(NotificationManager::class.java)?.apply {
|
||||
createNotificationChannel(
|
||||
NotificationChannel(
|
||||
@@ -136,8 +140,8 @@ object IconAdaptationTool {
|
||||
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 {
|
||||
@@ -176,8 +180,8 @@ object IconAdaptationTool {
|
||||
context.startActivity(
|
||||
Intent().apply {
|
||||
component = ComponentName(
|
||||
Const.MODULE_PACKAGE_NAME,
|
||||
"${Const.MODULE_PACKAGE_NAME}.ui.activity.auto.NotifyIconRuleUpdateActivity"
|
||||
MODULE_PACKAGE_NAME,
|
||||
"${MODULE_PACKAGE_NAME}.ui.activity.auto.NotifyIconRuleUpdateActivity"
|
||||
)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
|
@@ -75,9 +75,9 @@ object IconRuleManagerTool {
|
||||
/**
|
||||
* 从在线地址手动同步规则
|
||||
* @param context 实例
|
||||
* @param it 成功后回调
|
||||
* @param callback 成功后回调
|
||||
*/
|
||||
fun syncByHand(context: Context, it: () -> Unit) =
|
||||
fun syncByHand(context: Context, callback: () -> Unit) =
|
||||
context.showDialog {
|
||||
title = "同步列表"
|
||||
var sourceType = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY)
|
||||
@@ -116,12 +116,12 @@ object IconRuleManagerTool {
|
||||
confirmButton {
|
||||
context.modulePrefs.put(DataConst.SOURCE_SYNC_WAY, sourceType)
|
||||
context.modulePrefs.put(DataConst.SOURCE_SYNC_WAY_CUSTOM_URL, customUrl)
|
||||
sync(context, sourceType, customUrl, it)
|
||||
sync(context, sourceType, customUrl, callback)
|
||||
}
|
||||
cancelButton()
|
||||
neutralButton(text = "自定义规则") {
|
||||
context.showDialog {
|
||||
title = "自定义规则"
|
||||
title = "自定义规则(调试)"
|
||||
val editText = bind<DiaSourceFromStringBinding>().diaSfsInputEdit.apply {
|
||||
requestFocus()
|
||||
invalidate()
|
||||
@@ -139,7 +139,7 @@ object IconRuleManagerTool {
|
||||
)
|
||||
)
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> context.snake(msg = "请输入有效内容")
|
||||
}
|
||||
@@ -152,7 +152,7 @@ object IconRuleManagerTool {
|
||||
jsonString.isNotBlank() -> {
|
||||
params.save(dataJson = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]")
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> context.snake(msg = "请输入有效内容")
|
||||
}
|
||||
@@ -169,23 +169,23 @@ 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
|
||||
callback: () -> Unit
|
||||
) {
|
||||
when (sourceType) {
|
||||
TYPE_SOURCE_SYNC_WAY_1 ->
|
||||
onRefreshing(context, url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main", it)
|
||||
onRefreshing(context, url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main", callback)
|
||||
TYPE_SOURCE_SYNC_WAY_2 ->
|
||||
onRefreshing(context, url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main", it)
|
||||
onRefreshing(context, url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main", callback)
|
||||
TYPE_SOURCE_SYNC_WAY_3 ->
|
||||
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 +196,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 {
|
||||
@@ -216,7 +216,7 @@ object IconRuleManagerTool {
|
||||
params.save(it)
|
||||
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
|
||||
}
|
||||
@@ -225,7 +225,7 @@ object IconRuleManagerTool {
|
||||
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 +248,12 @@ 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()
|
||||
IconPackParams(context).also { params ->
|
||||
when {
|
||||
isDone -> when {
|
||||
@@ -265,7 +265,7 @@ object IconRuleManagerTool {
|
||||
params.save(content)
|
||||
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
|
||||
notifyRefresh(context)
|
||||
it()
|
||||
callback()
|
||||
}
|
||||
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
|
||||
}
|
||||
@@ -293,16 +293,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 +319,24 @@ object IconRuleManagerTool {
|
||||
}
|
||||
}
|
||||
} else baseCheckingInternetConnect(context) { isDone ->
|
||||
if (isDone) it() else pushNotify(context, title = "网络不可用", msg = "无法连接到互联网,无法更新通知图标规则")
|
||||
if (isDone) callback() else pushNotify(context, title = "网络不可用", msg = "无法连接到互联网,无法更新通知图标规则")
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查网络连接情况
|
||||
* @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,15 +347,15 @@ 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)
|
||||
(context as? Activity?)?.runOnUiThread { result(true, bodyString) } ?: result(true, bodyString)
|
||||
}
|
||||
})
|
||||
}.onFailure { it(false, "URL 无效") }
|
||||
}.onFailure { result(false, "URL 无效") }
|
||||
|
||||
/**
|
||||
* 推送通知图标更新通知
|
||||
|
@@ -22,59 +22,44 @@
|
||||
*/
|
||||
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.hook.HookConst.SYSTEMUI_PACKAGE_NAME
|
||||
import com.fankes.coloros.notify.utils.factory.*
|
||||
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.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData
|
||||
|
||||
/**
|
||||
* 系统界面工具
|
||||
*/
|
||||
object SystemUITool {
|
||||
|
||||
/** 宿主广播回调 */
|
||||
private var moduleHandlerCallback: ((Boolean, Boolean) -> Unit)? = null
|
||||
|
||||
/** 通知广播回调 */
|
||||
private var remindHandlerCallback: ((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)
|
||||
|
||||
/**
|
||||
* 注册广播
|
||||
* @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 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 实例
|
||||
*/
|
||||
fun unregister(context: Context) {
|
||||
context.unregisterReceiver(moduleHandlerReceiver)
|
||||
context.unregisterReceiver(remindHandlerReceiver)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查模块是否激活
|
||||
* @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(SYSTEMUI_PACKAGE_NAME).checkingVersionEquals(result)
|
||||
|
||||
/**
|
||||
* 重启系统界面
|
||||
@@ -86,9 +71,9 @@ object SystemUITool {
|
||||
msg = "你确定要立即重启系统界面吗?\n\n" +
|
||||
"重启过程会黑屏并等待进入锁屏重新解锁。"
|
||||
confirmButton {
|
||||
execShellSu(cmd = "pgrep systemui").also { pid ->
|
||||
execShell(cmd = "pgrep systemui").also { pid ->
|
||||
if (pid.isNotBlank())
|
||||
execShellSu(cmd = "kill -9 $pid")
|
||||
execShell(cmd = "kill -9 $pid")
|
||||
else toast(msg = "ROOT 权限获取失败")
|
||||
}
|
||||
}
|
||||
@@ -99,17 +84,10 @@ object SystemUITool {
|
||||
* 刷新系统界面状态栏与通知图标
|
||||
* @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)
|
||||
})
|
||||
}
|
||||
if (isXposedModuleActive)
|
||||
fun refreshSystemUI(context: Context? = null, isRefreshCacheOnly: Boolean = false, callback: () -> Unit = {}) = runInSafe {
|
||||
if (YukiHookAPI.Status.isXposedModuleActive)
|
||||
context?.showDialog {
|
||||
title = "请稍后"
|
||||
progressContent = "正在等待系统界面刷新"
|
||||
@@ -118,23 +96,29 @@ object SystemUITool {
|
||||
/** 设置等待延迟 */
|
||||
delayedRun(ms = 5000) {
|
||||
if (isWaited) return@delayedRun
|
||||
remindHandlerCallback = null
|
||||
cancel()
|
||||
context.snake(msg = "预计响应超时,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
|
||||
}
|
||||
remindHandlerCallback = { isGrasp, isValied ->
|
||||
remindHandlerCallback = null
|
||||
checkingActivated(context) { isValied ->
|
||||
when {
|
||||
isValied.not() -> {
|
||||
cancel()
|
||||
isWaited = true
|
||||
when {
|
||||
isGrasp && !isValied ->
|
||||
context.snake(msg = "请重启系统界面以生效模块更新", actionText = "立即重启") { restartSystemUI(context) }
|
||||
else -> it()
|
||||
}
|
||||
else -> context.dataChannel(SYSTEMUI_PACKAGE_NAME).with {
|
||||
wait(CALL_MODULE_REFRESH_RESULT) {
|
||||
cancel()
|
||||
isWaited = true
|
||||
callback()
|
||||
if (it.not()) context.snake(msg = "刷新失败,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
|
||||
}
|
||||
put(CALL_HOST_REFRESH_CACHING, isRefreshCacheOnly)
|
||||
}
|
||||
}
|
||||
}
|
||||
sendMessage()
|
||||
noCancelable()
|
||||
} ?: sendMessage()
|
||||
}
|
||||
else context?.snake(msg = "模块没有激活,更改不会生效")
|
||||
}
|
||||
|
||||
@@ -143,29 +127,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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/5/30.
|
||||
*/
|
||||
package com.fankes.coloros.notify.utils.tool
|
||||
|
||||
import android.content.Context
|
||||
import com.fankes.coloros.notify.utils.factory.openBrowser
|
||||
import com.fankes.coloros.notify.utils.factory.showDialog
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 的自动推广工具类
|
||||
*/
|
||||
object YukiPromoteTool {
|
||||
|
||||
/** 推广已读存储键值 */
|
||||
private val YUKI_PROMOTE_READED = PrefsData("yuki_promote_readed", false)
|
||||
|
||||
/**
|
||||
* 显示推广对话框
|
||||
* @param context 实例
|
||||
*/
|
||||
fun promote(context: Context) {
|
||||
fun saveReaded() = context.modulePrefs.put(YUKI_PROMOTE_READED, value = true)
|
||||
if (context.modulePrefs.get(YUKI_PROMOTE_READED).not())
|
||||
context.showDialog {
|
||||
title = "面向开发者的推广"
|
||||
msg = "你想快速拥有一个自己的 Xposed 模块吗,你只需要拥有基础的 Android 开发经验以及使用 Kotlin 编程语言即可。\n\n" +
|
||||
"快来体验 YukiHookAPI,这是一个使用 Kotlin 重新构建的高效 Xposed Hook API,助你的开发变得更轻松。"
|
||||
confirmButton(text = "去看看") {
|
||||
context.openBrowser(url = "https://github.com/fankes/YukiHookAPI")
|
||||
saveReaded()
|
||||
}
|
||||
cancelButton(text = "我不是开发者") { saveReaded() }
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
}
|
@@ -372,14 +372,14 @@
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="15dp">
|
||||
android:paddingTop="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|start">
|
||||
android:gravity="center|start"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="15dp"
|
||||
@@ -402,6 +402,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="启用 Android 12 通知栏风格"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/colorTextGray"
|
||||
@@ -413,9 +415,140 @@
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="无论任何版本,开启后都将使用 Android 12 原生通知图标方式为通知栏推送的通知图标增加外部圆形轮廓。\n目标为 Android 12 的系统将自动使用系统主题色着色。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<com.fankes.coloros.notify.ui.view.MaterialSwitch
|
||||
android:id="@+id/notify_media_panel_auto_exp_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="启用媒体通知播放时自动展开"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notify_media_panel_auto_exp_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="ColorOS 的媒体通知在播放的时候默认是折叠状态,开启后只要推送媒体通知以及恢复播放状态都会自动展开通知。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<com.fankes.coloros.notify.ui.view.MaterialSwitch
|
||||
android:id="@+id/notify_panel_config_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="自定义通知面板背景透明度"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSeekBar
|
||||
android:id="@+id/notify_panel_config_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:max="100"
|
||||
android:min="0"
|
||||
android:progress="75" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notify_panel_config_text_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:alpha="0.75"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxWidth="100dp"
|
||||
android:singleLine="true"
|
||||
android:text="当前"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="13.5sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notify_panel_config_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxWidth="100dp"
|
||||
android:singleLine="true"
|
||||
android:text="75%"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="开启自定义功能后,你可以拖拽滑动条来调整通知面板的透明度,0% 为全透明,100% 为不透明。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notify_panel_config_warn_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="此功能为实验性功能,后期不一定继续维护,且仅在 ColorOS 12.1 测试通过,不能保证可以在任何系统版本中生效,以及和各种通知栏主题模块配合使用,如有冲突请关闭此功能或禁用相关重复功能的主题模块。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:text="已知问题:\n(1) 悬浮通知也会被透明,在透明度较低的时候下拉会有阴影光晕,松手释放后恢复正常,后期看需求再解决。\n(2) 媒体通知可能在低于 ColorOS 12.1 的系统上无法生效透明度,旧版本不再适配,该问题不会被修复。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
@@ -430,15 +563,13 @@
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="10dp">
|
||||
android:paddingTop="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:gravity="center|start">
|
||||
|
||||
<ImageView
|
||||
@@ -461,8 +592,8 @@
|
||||
android:id="@+id/notify_icon_fix_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:text="启用通知图标优化"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
@@ -471,8 +602,8 @@
|
||||
android:id="@+id/notify_icon_fix_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_marginLeft="13dp"
|
||||
android:layout_marginRight="13dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/bg_button_round"
|
||||
android:gravity="center"
|
||||
@@ -485,8 +616,8 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
@@ -497,8 +628,8 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
@@ -506,12 +637,119 @@
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notify_icon_custom_corner_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:gravity="center|start"
|
||||
android:text="调整通知栏中的图标边框圆角大小"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSeekBar
|
||||
android:id="@+id/notify_icon_custom_corner_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:max="10"
|
||||
android:min="0"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:progress="10" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:alpha="0.75"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxWidth="100dp"
|
||||
android:singleLine="true"
|
||||
android:text="当前"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="13.5sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notify_icon_custom_corner_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxWidth="100dp"
|
||||
android:singleLine="true"
|
||||
android:text="10 dp"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="你可以拖拽滑动条来调整通知栏中图标的边框圆角大小,仅支持启用了 Android 12 通知栏风格的通知图标。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notify_icon_force_app_icon_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.fankes.coloros.notify.ui.view.MaterialSwitch
|
||||
android:id="@+id/notify_icon_force_app_icon_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:text="通知栏中的图标强制为 APP 图标"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="此选项默认关闭,开启后下拉通知栏中的通知图标将会被替换为 APP 自身图标,但是不会更改状态栏中的通知图标,这是一个破坏原生通知图标的行为,仅针对部分有需要的用户而添加,我们不推荐开启这个功能,请根据个人偏好进行选择是否需要开启。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notify_icon_fix_notify_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.fankes.coloros.notify.ui.view.MaterialSwitch
|
||||
@@ -538,8 +776,8 @@
|
||||
android:id="@+id/notify_icon_auto_sync_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
@@ -9,6 +9,17 @@
|
||||
android:paddingRight="15dp"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginBottom="17.5dp"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="此功能仅用于调试单条规则或多条规则,同步最新在线规则后这里的内容将会被覆盖清空。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
11
build.gradle
11
build.gradle
@@ -1,12 +1,13 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '7.1.2' apply false
|
||||
id 'com.android.library' version '7.1.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
|
||||
id 'com.android.application' version '7.2.0' apply false
|
||||
id 'com.android.library' version '7.2.0' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
appVersionName = "1.5"
|
||||
appVersionCode = 7
|
||||
appVersionName = "1.85"
|
||||
appVersionCode = 17
|
||||
enableR8 = true
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Sat Feb 26 13:04:39 CST 2022
|
||||
#Wed May 25 04:24:55 CST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
Reference in New Issue
Block a user