11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/1/24.
22 | */
23 | @file:Suppress("unused")
24 |
25 | package com.fankes.coloros.notify.application
26 |
27 | import androidx.appcompat.app.AppCompatDelegate
28 | import com.fankes.coloros.notify.data.ConfigData
29 | import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
30 |
31 | class CNNApplication : ModuleApplication() {
32 |
33 | override fun onCreate() {
34 | super.onCreate()
35 | /** 跟随系统夜间模式 */
36 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
37 | /** 装载存储控制类 */
38 | ConfigData.init(instance = this)
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/bean/IconDataBean.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/1/30.
22 | */
23 | package com.fankes.coloros.notify.bean
24 |
25 | import android.graphics.Bitmap
26 | import com.fankes.coloros.notify.utils.factory.base64
27 | import java.io.Serializable
28 |
29 | /**
30 | * 通知栏小图标 bean
31 | * @param appName APP 名称 - 仅限默认语言区域
32 | * @param packageName 包名
33 | * @param iconBitmap 图标位图
34 | * @param iconColor 通知栏中显示的图标颜色 - 设置为 0 使用系统默认颜色
35 | * @param contributorName 贡献者昵称
36 | * @param isEnabled 是否默认启用替换彩色图标 - 关闭后将全部停止替换
37 | * @param isEnabledAll 是否默认启用替换全部图标
38 | */
39 | data class IconDataBean(
40 | var appName: String,
41 | var packageName: String,
42 | var iconBitmap: Bitmap,
43 | var iconColor: Int = 0,
44 | var contributorName: String,
45 | var isEnabled: Boolean,
46 | var isEnabledAll: Boolean,
47 | ) : Serializable {
48 | fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
49 | fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all"
50 | override fun toString() = """
51 | {
52 | "appName": "$appName",
53 | "packageName": "$packageName",
54 | "iconBitmap": "${iconBitmap.base64}",
55 | "iconColor": "#${Integer.toHexString(iconColor)}",
56 | "contributorName": "$contributorName",
57 | "isEnabled": $isEnabled,
58 | "isEnabledAll": $isEnabledAll
59 | }
60 | """.trimIndent()
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/const/ConstFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2023/2/2.
22 | */
23 | @file:Suppress("MemberVisibilityCanBePrivate")
24 |
25 | package com.fankes.coloros.notify.const
26 |
27 | import com.fankes.coloros.notify.generated.AppProperties
28 | import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
29 |
30 | /**
31 | * 包名常量定义类
32 | */
33 | object PackageName {
34 |
35 | /** 系统框架 */
36 | const val SYSTEM_FRAMEWORK = "android"
37 |
38 | /** 系统界面 (系统 UI) */
39 | const val SYSTEMUI = "com.android.systemui"
40 | }
41 |
42 | /**
43 | * 通知图标优化名单同步方式定义类
44 | */
45 | object IconRuleSourceSyncType {
46 |
47 | /** GitHub Raw (代理 - GitHub Proxy) */
48 | const val GITHUB_RAW_PROXY_1 = 500
49 |
50 | /** GitHub Raw (代理 - 7ED Services) */
51 | const val GITHUB_RAW_PROXY_2 = 1000
52 |
53 | /** GitHub Raw (直连) */
54 | const val GITHUB_RAW_DIRECT = 2000
55 |
56 | /** 自定义地址 */
57 | const val CUSTOM_URL = 3000
58 | }
59 |
60 | /**
61 | * 模块版本常量定义类
62 | */
63 | object ModuleVersion {
64 |
65 | /** 当前 GitHub 提交的 ID (CI 自动构建) */
66 | const val GITHUB_COMMIT_ID = AppProperties.GITHUB_CI_COMMIT_ID
67 |
68 | /** 版本名称 */
69 | const val NAME = BuildConfigWrapper.VERSION_NAME
70 |
71 | /** 版本号 */
72 | const val CODE = BuildConfigWrapper.VERSION_CODE
73 |
74 | /** 是否为 CI 自动构建版本 */
75 | val isCiMode = GITHUB_COMMIT_ID.isNotBlank()
76 |
77 | /** 当前版本名称后缀 */
78 | val suffix = GITHUB_COMMIT_ID.let { if (it.isNotBlank()) "-$it" else "" }
79 |
80 | override fun toString() = "$NAME$suffix($CODE)"
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/data/ConfigData.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2023/2/2.
22 | */
23 | @file:Suppress("MemberVisibilityCanBePrivate")
24 |
25 | package com.fankes.coloros.notify.data
26 |
27 | import android.content.Context
28 | import com.fankes.coloros.notify.const.IconRuleSourceSyncType
29 | import com.fankes.coloros.notify.utils.factory.isUpperOfAndroidS
30 | import com.highcapable.yukihookapi.hook.factory.prefs
31 | import com.highcapable.yukihookapi.hook.log.YLog
32 | import com.highcapable.yukihookapi.hook.param.PackageParam
33 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
34 |
35 | /**
36 | * 全局配置存储控制类
37 | */
38 | object ConfigData {
39 |
40 | /** 启用模块 */
41 | val ENABLE_MODULE = PrefsData("_enable_module", true)
42 |
43 | /** 启用模块日志 */
44 | val ENABLE_MODULE_LOG = PrefsData("_enable_module_log", false)
45 |
46 | /** 启用通知图标兼容模式 */
47 | val ENABLE_COLOR_ICON_COMPAT = PrefsData("_color_icon_compat", false)
48 |
49 | /** 移除开发者选项警告通知 */
50 | val ENABLE_REMOVE_DEV_NOTIFY = PrefsData("_remove_dev_notify", true)
51 |
52 | /** 移除充电完成通知 */
53 | val ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY = PrefsData("_remove_charge_complete_notify", false)
54 |
55 | /** 移除免打扰通知 */
56 | val ENABLE_REMOVE_DND_ALERT_NOTIFY = PrefsData("_remove_dndalert_notify", false)
57 |
58 | /** 启用 Material 3 通知图标风格 */
59 | val ENABLE_MD3_NOTIFY_ICON_STYLE = PrefsData("_notify_icon_md3_style", isUpperOfAndroidS)
60 |
61 | /** 通知栏中的通知图标圆角程度 */
62 | val NOTIFY_ICON_CORNER_SIZE = PrefsData("_notify_icon_corner", 15)
63 |
64 | /** 强制通知栏中的通知图标使用系统着色 */
65 | val ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR = PrefsData("_notify_icon_force_system_color", false)
66 |
67 | /** 强制通知栏中的通知图标为 APP 图标 */
68 | val ENABLE_NOTIFY_ICON_FORCE_APP_ICON = PrefsData("_notify_icon_force_app_icon", false)
69 |
70 | /** 启用媒体通知播放时自动展开 */
71 | val ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP = PrefsData("_enable_notify_media_panel_auto_exp", false)
72 |
73 | /** 启用自定义通知面板背景透明度 */
74 | val ENABLE_NOTIFY_PANEL_ALPHA = PrefsData("_enable_notify_panel_alpha_pst", false)
75 |
76 | /** 自定义通知面板背景透明度 */
77 | val NOTIFY_PANEL_ALPHA_LEVEL = PrefsData("_notify_panel_alpha_pst", 75)
78 |
79 | /** 启用通知图标优化 */
80 | val ENABLE_NOTIFY_ICON_FIX = PrefsData("_notify_icon_fix", true)
81 |
82 | /** 使用占位符修补未适配的通知图标 */
83 | val ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER = PrefsData("_notify_icon_fix_placeholder", false)
84 |
85 | /** 提醒未适配通知图标的新安装应用 */
86 | val ENABLE_NOTIFY_ICON_FIX_NOTIFY = PrefsData("_notify_icon_fix_notify", true)
87 |
88 | /** 启用通知图标优化名单自动更新 */
89 | val ENABLE_NOTIFY_ICON_FIX_AUTO = PrefsData("_enable_notify_icon_fix_auto", true)
90 |
91 | /** 通知图标优化名单自动更新时间 */
92 | val NOTIFY_ICON_FIX_AUTO_TIME = PrefsData("_notify_icon_fix_auto_time", "07:00")
93 |
94 | /** 通知图标优化适配数据 */
95 | val NOTIFY_ICONS_DATA = PrefsData("_notify_icon_datas", "")
96 |
97 | /** 通知图标优化名单同步方式 */
98 | val ICON_RULE_SOURCE_SYNC_TYPE = PrefsData("_rule_source_sync_way", IconRuleSourceSyncType.GITHUB_RAW_PROXY_1)
99 |
100 | /** 通知图标优化名单同步地址 */
101 | val ICON_RULE_SOURCE_SYNC_CUSTOM_URL = PrefsData("_rule_source_sync_way_custom_url", "")
102 |
103 | /** 当前实例 - [Context] or [PackageParam] */
104 | private var instance: Any? = null
105 |
106 | /**
107 | * 初始化存储控制类
108 | * @param instance 实例 - 只能是 [Context] or [PackageParam]
109 | * @throws IllegalStateException 如果类型错误
110 | */
111 | fun init(instance: Any) {
112 | when (instance) {
113 | is Context, is PackageParam -> this.instance = instance
114 | else -> error("Unknown type for init ConfigData")
115 | }
116 | }
117 |
118 | /**
119 | * 读取 [String] 数据
120 | * @param data 键值数据模板
121 | * @return [String]
122 | */
123 | private fun getString(data: PrefsData) = when (instance) {
124 | is Context -> (instance as Context).prefs().get(data)
125 | is PackageParam -> (instance as PackageParam).prefs.get(data)
126 | else -> error("Unknown type for get prefs data")
127 | }
128 |
129 | /**
130 | * 存入 [String] 数据
131 | * @param data 键值数据模板
132 | * @param value 键值内容
133 | */
134 | private fun putString(data: PrefsData, value: String) {
135 | when (instance) {
136 | is Context -> (instance as Context).prefs().edit { put(data, value) }
137 | is PackageParam -> YLog.warn("Not support for this method")
138 | else -> error("Unknown type for put prefs data")
139 | }
140 | }
141 |
142 | /**
143 | * 读取 [Int] 数据
144 | * @param data 键值数据模板
145 | * @return [Int]
146 | */
147 | internal fun getInt(data: PrefsData) = when (instance) {
148 | is Context -> (instance as Context).prefs().get(data)
149 | is PackageParam -> (instance as PackageParam).prefs.get(data)
150 | else -> error("Unknown type for get prefs data")
151 | }
152 |
153 | /**
154 | * 存入 [Int] 数据
155 | * @param data 键值数据模板
156 | * @param value 键值内容
157 | */
158 | internal fun putInt(data: PrefsData, value: Int) {
159 | when (instance) {
160 | is Context -> (instance as Context).prefs().edit { put(data, value) }
161 | is PackageParam -> YLog.warn("Not support for this method")
162 | else -> error("Unknown type for put prefs data")
163 | }
164 | }
165 |
166 | /**
167 | * 读取 [Boolean] 数据
168 | * @param data 键值数据模板
169 | * @return [Boolean]
170 | */
171 | internal fun getBoolean(data: PrefsData) = when (instance) {
172 | is Context -> (instance as Context).prefs().get(data)
173 | is PackageParam -> (instance as PackageParam).prefs.get(data)
174 | else -> error("Unknown type for get prefs data")
175 | }
176 |
177 | /**
178 | * 存入 [Boolean] 数据
179 | * @param data 键值数据模板
180 | * @param value 键值内容
181 | */
182 | internal fun putBoolean(data: PrefsData, value: Boolean) {
183 | when (instance) {
184 | is Context -> (instance as Context).prefs().edit { put(data, value) }
185 | is PackageParam -> YLog.warn("Not support for this method")
186 | else -> error("Unknown type for put prefs data")
187 | }
188 | }
189 |
190 | /**
191 | * 是否启用模块
192 | * @return [Boolean]
193 | */
194 | var isEnableModule
195 | get() = getBoolean(ENABLE_MODULE)
196 | set(value) {
197 | putBoolean(ENABLE_MODULE, value)
198 | }
199 |
200 | /**
201 | * 是否启用模块日志
202 | * @return [Boolean]
203 | */
204 | var isEnableModuleLog
205 | get() = getBoolean(ENABLE_MODULE_LOG)
206 | set(value) {
207 | putBoolean(ENABLE_MODULE_LOG, value)
208 | }
209 |
210 | /**
211 | * 是否启用通知图标兼容模式
212 | * @return [Boolean]
213 | */
214 | var isEnableColorIconCompat
215 | get() = getBoolean(ENABLE_COLOR_ICON_COMPAT)
216 | set(value) {
217 | putBoolean(ENABLE_COLOR_ICON_COMPAT, value)
218 | }
219 |
220 | /**
221 | * 是否移除开发者选项警告通知
222 | * @return [Boolean]
223 | */
224 | var isEnableRemoveDevNotify
225 | get() = getBoolean(ENABLE_REMOVE_DEV_NOTIFY)
226 | set(value) {
227 | putBoolean(ENABLE_REMOVE_DEV_NOTIFY, value)
228 | }
229 |
230 | /**
231 | * 是否移除充电完成通知
232 | * @return [Boolean]
233 | */
234 | var isEnableRemoveChangeCompleteNotify
235 | get() = getBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY)
236 | set(value) {
237 | putBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY, value)
238 | }
239 |
240 | /**
241 | * 是否移除免打扰通知
242 | * @return [Boolean]
243 | */
244 | var isEnableRemoveDndAlertNotify
245 | get() = getBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY)
246 | set(value) {
247 | putBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY, value)
248 | }
249 |
250 | /**
251 | * 是否启用 material 3 通知图标风格
252 | * @return [Boolean]
253 | */
254 | var isEnableMd3NotifyIconStyle
255 | get() = getBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE)
256 | set(value) {
257 | putBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE, value)
258 | }
259 |
260 | /**
261 | * 通知栏中的通知图标圆角程度
262 | * @return [Int]
263 | */
264 | var notifyIconCornerSize
265 | get() = getInt(NOTIFY_ICON_CORNER_SIZE)
266 | set(value) {
267 | putInt(NOTIFY_ICON_CORNER_SIZE, value)
268 | }
269 |
270 | /**
271 | * 是否强制通知栏中的通知图标使用系统着色
272 | * @return [Boolean]
273 | */
274 | var isEnableNotifyIconForceSystemColor
275 | get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR)
276 | set(value) {
277 | putBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR, value)
278 | }
279 |
280 | /**
281 | * 是否强制通知栏中的通知图标为 APP 图标
282 | * @return [Boolean]
283 | */
284 | var isEnableNotifyIconForceAppIcon
285 | get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON)
286 | set(value) {
287 | putBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON, value)
288 | }
289 |
290 | /**
291 | * 是否启用媒体通知播放时自动展开
292 | * @return [Boolean]
293 | */
294 | var isEnableNotifyMediaPanelAutoExp
295 | get() = getBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP)
296 | set(value) {
297 | putBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP, value)
298 | }
299 |
300 | /**
301 | * 是否启用自定义通知面板背景透明度
302 | * @return [Boolean]
303 | */
304 | var isEnableNotifyPanelAlpha
305 | get() = getBoolean(ENABLE_NOTIFY_PANEL_ALPHA)
306 | set(value) {
307 | putBoolean(ENABLE_NOTIFY_PANEL_ALPHA, value)
308 | }
309 |
310 | /**
311 | * 自定义通知面板背景透明度
312 | * @return [Int]
313 | */
314 | var notifyPanelAlphaLevel
315 | get() = getInt(NOTIFY_PANEL_ALPHA_LEVEL)
316 | set(value) {
317 | putInt(NOTIFY_PANEL_ALPHA_LEVEL, value)
318 | }
319 |
320 | /**
321 | * 是否启用通知图标优化
322 | * @return [Boolean]
323 | */
324 | var isEnableNotifyIconFix
325 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX)
326 | set(value) {
327 | putBoolean(ENABLE_NOTIFY_ICON_FIX, value)
328 | }
329 |
330 | /**
331 | * 是否使用占位符修补未适配的通知图标
332 | * @return [Boolean]
333 | */
334 | var isEnableNotifyIconFixPlaceholder
335 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER)
336 | set(value) {
337 | putBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER, value)
338 | }
339 |
340 | /**
341 | * 是否提醒未适配通知图标的新安装应用
342 | * @return [Boolean]
343 | */
344 | var isEnableNotifyIconFixNotify
345 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY)
346 | set(value) {
347 | putBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, value)
348 | }
349 |
350 | /**
351 | * 是否启用通知图标优化名单自动更新
352 | * @return [Boolean]
353 | */
354 | var isEnableNotifyIconFixAuto
355 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO)
356 | set(value) {
357 | putBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO, value)
358 | }
359 |
360 | /**
361 | * 通知图标优化名单自动更新时间
362 | * @return [String]
363 | */
364 | var notifyIconFixAutoTime
365 | get() = getString(NOTIFY_ICON_FIX_AUTO_TIME)
366 | set(value) {
367 | putString(NOTIFY_ICON_FIX_AUTO_TIME, value)
368 | }
369 |
370 | /**
371 | * 通知图标优化名单同步方式
372 | * @return [Int]
373 | */
374 | var iconRuleSourceSyncType
375 | get() = getInt(ICON_RULE_SOURCE_SYNC_TYPE)
376 | set(value) {
377 | putInt(ICON_RULE_SOURCE_SYNC_TYPE, value)
378 | }
379 |
380 | /**
381 | * 通知图标优化名单同步地址
382 | * @return [String]
383 | */
384 | var iconRuleSourceSyncCustomUrl
385 | get() = getString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL)
386 | set(value) {
387 | putString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL, value)
388 | }
389 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/data/factory/CompoundButtonFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2023/2/3.
22 | */
23 | @file:Suppress("unused", "MemberVisibilityCanBePrivate")
24 |
25 | package com.fankes.coloros.notify.data.factory
26 |
27 | import android.widget.CompoundButton
28 | import com.fankes.coloros.notify.data.ConfigData
29 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
30 |
31 | /**
32 | * 绑定到 [CompoundButton] 自动设置选中状态
33 | * @param data 键值数据模板
34 | * @param initiate 方法体
35 | */
36 | fun CompoundButton.bind(data: PrefsData, initiate: CompoundButtonDataBinder.(CompoundButton) -> Unit = {}) {
37 | val binder = CompoundButtonDataBinder(button = this).also { initiate(it, this) }
38 | isChecked = ConfigData.getBoolean(data).also { binder.initializeCallback?.invoke(it) }
39 | binder.applyChangesCallback = { ConfigData.putBoolean(data, it) }
40 | setOnCheckedChangeListener { button, isChecked ->
41 | if (button.isPressed) {
42 | if (binder.isAutoApplyChanges) binder.applyChangesCallback?.invoke(isChecked)
43 | binder.changedCallback?.invoke(isChecked)
44 | }
45 | }
46 | }
47 |
48 | /**
49 | * [CompoundButton] 数据绑定管理器实例
50 | * @param button 当前实例
51 | */
52 | class CompoundButtonDataBinder(private val button: CompoundButton) {
53 |
54 | /** 状态初始化回调事件 */
55 | internal var initializeCallback: ((Boolean) -> Unit)? = null
56 |
57 | /** 状态改变回调事件 */
58 | internal var changedCallback: ((Boolean) -> Unit)? = null
59 |
60 | /** 应用更改回调事件 */
61 | internal var applyChangesCallback: ((Boolean) -> Unit)? = null
62 |
63 | /** 是否启用自动应用更改 */
64 | var isAutoApplyChanges = true
65 |
66 | /**
67 | * 监听状态初始化
68 | * @param result 回调结果
69 | */
70 | fun onInitialize(result: (Boolean) -> Unit) {
71 | initializeCallback = result
72 | }
73 |
74 | /**
75 | * 监听状态改变
76 | * @param result 回调结果
77 | */
78 | fun onChanged(result: (Boolean) -> Unit) {
79 | changedCallback = result
80 | }
81 |
82 | /** 重新初始化 */
83 | fun reinitialize() {
84 | initializeCallback?.invoke(button.isChecked)
85 | }
86 |
87 | /** 应用更改并重新初始化 */
88 | fun applyChangesAndReinitialize() {
89 | applyChanges()
90 | reinitialize()
91 | }
92 |
93 | /** 应用更改 */
94 | fun applyChanges() {
95 | applyChangesCallback?.invoke(button.isChecked)
96 | }
97 |
98 | /** 取消更改 */
99 | fun cancelChanges() {
100 | button.isChecked = button.isChecked.not()
101 | }
102 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/data/factory/SeekBarFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2023/2/4.
22 | */
23 | @file:Suppress("SetTextI18n")
24 |
25 | package com.fankes.coloros.notify.data.factory
26 |
27 | import android.widget.SeekBar
28 | import android.widget.TextView
29 | import com.fankes.coloros.notify.data.ConfigData
30 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
31 |
32 | /**
33 | * 绑定到 [SeekBar] 自动设置进度与 [TextView]
34 | * @param data 键值数据模板
35 | * @param textView 文本框
36 | * @param suffix 文本显示的后缀 - 默认空
37 | * @param onChange 当改变停止时回调
38 | */
39 | fun SeekBar.bind(data: PrefsData, textView: TextView, suffix: String = "", onChange: (Int) -> Unit = {}) {
40 | ConfigData.getInt(data).also { value ->
41 | textView.text = "$value$suffix"
42 | progress = value
43 | }
44 | setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
45 |
46 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
47 | textView.text = "$progress$suffix"
48 | }
49 |
50 | override fun onStopTrackingTouch(seekBar: SeekBar) {
51 | ConfigData.putInt(data, seekBar.progress)
52 | onChange(seekBar.progress)
53 | }
54 |
55 | override fun onStartTrackingTouch(seekBar: SeekBar?) {}
56 | })
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/hook/HookEntry.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/2/26.
22 | */
23 | package com.fankes.coloros.notify.hook
24 |
25 | import com.fankes.coloros.notify.const.PackageName
26 | import com.fankes.coloros.notify.data.ConfigData
27 | import com.fankes.coloros.notify.hook.entity.FrameworkHooker
28 | import com.fankes.coloros.notify.hook.entity.SystemUIHooker
29 | import com.fankes.coloros.notify.utils.factory.isNotColorOS
30 | import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
31 | import com.highcapable.yukihookapi.hook.factory.configs
32 | import com.highcapable.yukihookapi.hook.factory.encase
33 | import com.highcapable.yukihookapi.hook.log.YLog
34 | import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
35 |
36 | @InjectYukiHookWithXposed
37 | object HookEntry : IYukiHookXposedInit {
38 |
39 | override fun onInit() = configs {
40 | debugLog {
41 | tag = "ColorOSNotifyIcon"
42 | isRecord = true
43 | elements(PRIORITY)
44 | }
45 | isDebug = false
46 | }
47 |
48 | override fun onHook() = encase {
49 | if (isNotColorOS) return@encase YLog.warn("Aborted Hook -> This System is not ColorOS")
50 | loadSystem(FrameworkHooker)
51 | loadApp(PackageName.SYSTEMUI) {
52 | ConfigData.init(instance = this)
53 | if (ConfigData.isEnableModule)
54 | loadHooker(SystemUIHooker)
55 | else YLog.warn("Aborted Hook -> Hook Closed")
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/hook/entity/FrameworkHooker.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by Nep-Timeline on 2025/5/27.
22 | */
23 | package com.fankes.coloros.notify.hook.entity
24 |
25 | import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
26 | import com.highcapable.yukihookapi.hook.factory.method
27 | import com.highcapable.yukihookapi.hook.type.android.NotificationClass
28 | import com.highcapable.yukihookapi.hook.type.java.BooleanType
29 | import com.highcapable.yukihookapi.hook.type.java.StringClass
30 |
31 | /**
32 | * 系统框架核心 Hook 类
33 | */
34 | object FrameworkHooker : YukiBaseHooker() {
35 |
36 | /** ColorOS 存在的类 - 旧版本不存在 */
37 | private val OplusNotificationFixHelperClass by lazyClassOrNull("com.android.server.notification.OplusNotificationFixHelper")
38 |
39 | override fun onHook() {
40 | /** 拦截 ColorOS 覆盖应用通知图标 */
41 | OplusNotificationFixHelperClass?.method {
42 | name = "fixSmallIcon"
43 | param(NotificationClass, StringClass, StringClass, BooleanType)
44 | }?.ignored()?.hook()?.intercept()
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/param/IconPackParams.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/1/24.
22 | */
23 | @file:Suppress("MemberVisibilityCanBePrivate")
24 |
25 | package com.fankes.coloros.notify.param
26 |
27 | import android.content.Context
28 | import android.graphics.Color
29 | import com.fankes.coloros.notify.bean.IconDataBean
30 | import com.fankes.coloros.notify.data.ConfigData
31 | import com.fankes.coloros.notify.utils.factory.bitmap
32 | import com.fankes.coloros.notify.utils.factory.safeOf
33 | import com.fankes.coloros.notify.utils.factory.safeOfNan
34 | import com.fankes.coloros.notify.utils.factory.safeOfNull
35 | import com.fankes.coloros.notify.utils.factory.snake
36 | import com.highcapable.yukihookapi.hook.factory.prefs
37 | import com.highcapable.yukihookapi.hook.param.PackageParam
38 | import org.json.JSONArray
39 | import org.json.JSONObject
40 |
41 | /**
42 | * 通知栏小图标适配类
43 | *
44 | * 国内 APP 不规范的图标将由这里完成其自定义单色小图标绘制
45 | * @param context 实例 - 二选一
46 | * @param param 实例 - 二选一
47 | */
48 | class IconPackParams(private val context: Context? = null, private val param: PackageParam? = null) {
49 |
50 | /**
51 | * 已存储的 JSON 数据
52 | * @return [String]
53 | */
54 | internal val storageDataJson get() = (context?.prefs() ?: param?.prefs)?.get(ConfigData.NOTIFY_ICONS_DATA)
55 |
56 | /**
57 | * 获取图标数据
58 | * @return [Array] 通知栏小图标数组
59 | */
60 | val iconDatas
61 | get() = ArrayList().apply {
62 | storageDataJson?.also {
63 | if (it.isNotBlank()) runCatching {
64 | JSONArray(it).also { array ->
65 | for (i in 0 until array.length()) runCatching {
66 | add(convertToBean(array.get(i) as JSONObject)!!)
67 | }.onFailure { context?.snake(msg = "部分规则加载失败") }
68 | }
69 | }.onFailure { context?.snake(msg = "规则加载发生错误") }
70 | }
71 | }
72 |
73 | /**
74 | * 转换为 [IconDataBean]
75 | * @param jsonObject Json 实例
76 | * @return [IconDataBean] or null
77 | */
78 | private fun convertToBean(jsonObject: JSONObject) = safeOfNull {
79 | jsonObject.let {
80 | IconDataBean(
81 | appName = it.getString("appName"),
82 | packageName = it.getString("packageName"),
83 | isEnabled = it.getBoolean("isEnabled"),
84 | isEnabledAll = it.getBoolean("isEnabledAll"),
85 | iconBitmap = it.getString("iconBitmap").bitmap,
86 | iconColor = safeOfNan { Color.parseColor(it.getString("iconColor")) },
87 | contributorName = it.getString("contributorName")
88 | )
89 | }
90 | }
91 |
92 | /**
93 | * 拼接图标数组数据
94 | * @param dataJson1 图标数据 JSON
95 | * @param dataJson2 图标数据 JSON
96 | * @return [String] 拼接后的数据
97 | */
98 | fun splicingJsonArray(dataJson1: String, dataJson2: String) = safeOf(default = "[]") {
99 | dataJson1.replace("]", "") + "," + dataJson2.replace("[", "")
100 | }
101 |
102 | /**
103 | * 是否不为合法 JSON
104 | * @param json 数据
105 | * @return [Boolean]
106 | */
107 | fun isNotVaildJson(json: String) = !isJsonArray(json) && !isJsonObject(json)
108 |
109 | /**
110 | * 是否为 JSON 数组
111 | * @param json 数据
112 | * @return [Boolean]
113 | */
114 | fun isJsonArray(json: String) = json.trim().let { it.startsWith("[") && it.endsWith("]") }
115 |
116 | /**
117 | * 是否为 JSON 实例
118 | * @param json 数据
119 | * @return [Boolean]
120 | */
121 | fun isJsonObject(json: String) = json.trim().let { it.startsWith("{") && it.endsWith("}") }
122 |
123 | /**
124 | * 是否为异常地址
125 | * @param json 数据
126 | * @return [Boolean]
127 | */
128 | fun isHackString(json: String) = json.contains("Checking your browser before accessing")
129 |
130 | /**
131 | * 比较图标数据不相等
132 | * @param dataJson 图标数据 JSON
133 | * @return [Boolean] 是否不相等
134 | */
135 | fun isCompareDifferent(dataJson: String) = storageDataJson?.trim() != dataJson.trim()
136 |
137 | /**
138 | * 保存图标数据
139 | * @param dataJson 图标数据 JSON
140 | */
141 | fun save(dataJson: String) = context?.prefs()?.edit { put(ConfigData.NOTIFY_ICONS_DATA, dataJson) }
142 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/param/factory/DataFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/2/15.
22 | * This file is Modified by fankes on 2023/2/3.
23 | */
24 | package com.fankes.coloros.notify.param.factory
25 |
26 | import android.content.Context
27 | import com.fankes.coloros.notify.bean.IconDataBean
28 | import com.highcapable.yukihookapi.hook.factory.prefs
29 | import com.highcapable.yukihookapi.hook.param.PackageParam
30 |
31 | /**
32 | * 获取此 APP 的通知图标是否被 Hook
33 | * @param bean 图标 bean
34 | */
35 | fun PackageParam.isAppNotifyHookOf(bean: IconDataBean) = prefs.getBoolean(bean.toEnabledName(), bean.isEnabled)
36 |
37 | /**
38 | * 获取此 APP 的通知图标是否被 Hook
39 | * @param bean 图标 bean
40 | */
41 | fun Context.isAppNotifyHookOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledName(), bean.isEnabled)
42 |
43 | /**
44 | * 设置 Hook 此 APP 的通知图标
45 | * @param bean 图标 bean
46 | * @param isHook 是否 Hook
47 | */
48 | fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledName(), isHook) }
49 |
50 | /**
51 | * 获取此 APP 的通知图标是否被全部 Hook
52 | * @param bean 图标 bean
53 | */
54 | fun PackageParam.isAppNotifyHookAllOf(bean: IconDataBean) = prefs.getBoolean(bean.toEnabledAllName(), bean.isEnabledAll)
55 |
56 | /**
57 | * 获取此 APP 的通知图标是否被全部 Hook
58 | * @param bean 图标 bean
59 | */
60 | fun Context.isAppNotifyHookAllOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledAllName(), bean.isEnabledAll)
61 |
62 | /**
63 | * 设置全部 Hook 此 APP 的通知图标
64 | * @param bean 图标 bean
65 | * @param isHook 是否 Hook
66 | */
67 | fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledAllName(), isHook) }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/service/QuickStartTileService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/3/26.
22 | */
23 | package com.fankes.coloros.notify.service
24 |
25 | import android.service.quicksettings.TileService
26 | import com.fankes.coloros.notify.ui.activity.ConfigureActivity
27 | import com.fankes.coloros.notify.utils.factory.navigate
28 |
29 | class QuickStartTileService : TileService() {
30 |
31 | override fun onClick() {
32 | super.onClick()
33 | /** 启动通知图标优化列表窗口 */
34 | navigate()
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/ui/activity/ConfigureActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/3/26.
22 | */
23 | @file:Suppress("DEPRECATION")
24 |
25 | package com.fankes.coloros.notify.ui.activity.auto
26 |
27 | import android.app.Activity
28 | import android.os.Bundle
29 | import android.view.View
30 | import android.view.WindowManager
31 | import com.fankes.coloros.notify.ui.activity.base.BaseActivity
32 | import com.fankes.coloros.notify.utils.factory.delayedRun
33 | import com.fankes.coloros.notify.utils.tool.IconRuleManagerTool
34 | import com.fankes.coloros.notify.utils.tool.SystemUITool
35 |
36 | class NotifyIconRuleUpdateActivity : Activity() {
37 |
38 | override fun onCreate(savedInstanceState: Bundle?) {
39 | super.onCreate(savedInstanceState)
40 | /** 设置透明窗口 */
41 | window?.decorView?.systemUiVisibility =
42 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
43 | window?.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
44 | window?.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
45 | /** 检测运行状态 */
46 | if (BaseActivity.isMainThreadRunning) {
47 | finish()
48 | return
49 | }
50 | /** 拉取云端数据 */
51 | IconRuleManagerTool.sync(context = this) {
52 | /** 刷新系统界面 */
53 | SystemUITool.refreshSystemUI()
54 | /** 结束当前窗口 */
55 | runOnUiThread { delayedRun(ms = 1000) { finish() } }
56 | }
57 | /** 切换到后台 */
58 | moveTaskToBack(true)
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/ui/activity/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/1/30.
22 | */
23 | @file:Suppress("DEPRECATION")
24 |
25 | package com.fankes.coloros.notify.ui.activity.base
26 |
27 | import android.os.Build
28 | import android.os.Bundle
29 | import androidx.appcompat.app.AppCompatActivity
30 | import androidx.core.content.res.ResourcesCompat
31 | import androidx.core.view.WindowCompat
32 | import androidx.viewbinding.ViewBinding
33 | import com.fankes.coloros.notify.R
34 | import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode
35 | import com.highcapable.yukihookapi.hook.factory.current
36 | import com.highcapable.yukihookapi.hook.factory.method
37 | import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
38 |
39 | abstract class BaseActivity : AppCompatActivity() {
40 |
41 | companion object {
42 |
43 | /** 应用是否正在运行 */
44 | var isMainThreadRunning = false
45 | }
46 |
47 | /** 获取绑定布局对象 */
48 | lateinit var binding: VB
49 |
50 | override fun onCreate(savedInstanceState: Bundle?) {
51 | super.onCreate(savedInstanceState)
52 | isMainThreadRunning = true
53 | binding = current().generic()?.argument()?.method {
54 | name = "inflate"
55 | param(LayoutInflaterClass)
56 | }?.get()?.invoke(layoutInflater) ?: error("binding failed")
57 | if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
58 | setContentView(binding.root)
59 | /** 隐藏系统的标题栏 */
60 | supportActionBar?.hide()
61 | /** 初始化沉浸状态栏 */
62 | WindowCompat.getInsetsController(window, window.decorView).apply {
63 | isAppearanceLightStatusBars = isNotSystemInDarkMode
64 | isAppearanceLightNavigationBars = isNotSystemInDarkMode
65 | }
66 | ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
67 | window?.statusBarColor = it
68 | window?.navigationBarColor = it
69 | window?.navigationBarDividerColor = it
70 | }
71 | /** 装载子类 */
72 | onCreate()
73 | }
74 |
75 | /** 回调 [onCreate] 方法 */
76 | abstract fun onCreate()
77 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/ui/widget/MaterialSwitch.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/1/8.
22 | */
23 | @file:Suppress("SameParameterValue")
24 |
25 | package com.fankes.coloros.notify.ui.widget
26 |
27 | import android.content.Context
28 | import android.content.res.ColorStateList
29 | import android.graphics.Color
30 | import android.text.TextUtils
31 | import android.util.AttributeSet
32 | import androidx.appcompat.widget.SwitchCompat
33 | import com.fankes.coloros.notify.utils.factory.dp
34 | import com.fankes.coloros.notify.utils.factory.isSystemInDarkMode
35 | import top.defaults.drawabletoolbox.DrawableBuilder
36 |
37 | class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {
38 |
39 | private fun toColors(selected: Int, pressed: Int, normal: Int): ColorStateList {
40 | val colors = intArrayOf(selected, pressed, normal)
41 | val states = arrayOfNulls(3)
42 | states[0] = intArrayOf(android.R.attr.state_checked)
43 | states[1] = intArrayOf(android.R.attr.state_pressed)
44 | states[2] = intArrayOf()
45 | return ColorStateList(states, colors)
46 | }
47 |
48 | private val thumbColor get() = if (context.isSystemInDarkMode) 0xFF7C7C7C else 0xFFCCCCCC
49 |
50 | init {
51 | trackDrawable = DrawableBuilder()
52 | .rectangle()
53 | .rounded()
54 | .solidColor(0xFF656565.toInt())
55 | .height(20.dp(context))
56 | .cornerRadius(15.dp(context))
57 | .build()
58 | thumbDrawable = DrawableBuilder()
59 | .rectangle()
60 | .rounded()
61 | .solidColor(Color.WHITE)
62 | .size(20.dp(context), 20.dp(context))
63 | .cornerRadius(20.dp(context))
64 | .strokeWidth(8.dp(context))
65 | .strokeColor(Color.TRANSPARENT)
66 | .build()
67 | trackTintList = toColors(
68 | 0xFF656565.toInt(),
69 | thumbColor.toInt(),
70 | thumbColor.toInt()
71 | )
72 | isSingleLine = true
73 | ellipsize = TextUtils.TruncateAt.END
74 | }
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/factory/BackPressedEventFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/10/6.
22 | */
23 | package com.fankes.coloros.notify.utils.factory
24 |
25 | import androidx.activity.OnBackPressedCallback
26 | import androidx.appcompat.app.AppCompatActivity
27 |
28 | /** 已添加的返回监听事件 */
29 | private val onBackPressedCallbacks = HashMap()
30 |
31 | /**
32 | * 手动调用返回事件
33 | * @param ignored 是否忽略现有返回监听事件立即返回 - 否则将执行返回事件 - 默认否
34 | */
35 | fun AppCompatActivity.callOnBackPressed(ignored: Boolean = false) {
36 | if (isDestroyed) return
37 | onBackPressedCallbacks[this]?.isEnabled = ignored.not()
38 | onBackPressedDispatcher.onBackPressed()
39 | }
40 |
41 | /**
42 | * 添加返回监听事件
43 | * @param callback 回调事件
44 | */
45 | fun AppCompatActivity.addOnBackPressedEvent(callback: OnBackPressedEvent.() -> Unit) {
46 | object : OnBackPressedCallback(true) {
47 | override fun handleOnBackPressed() {
48 | OnBackPressedEvent(this@addOnBackPressedEvent).apply(callback)
49 | }
50 | }.also { result ->
51 | onBackPressedCallbacks.computeIfAbsent(this) {
52 | onBackPressedDispatcher.addCallback(result)
53 | result
54 | }
55 | }
56 | }
57 |
58 | /**
59 | * 返回监听事件实现类
60 | * @param instance 当前实例
61 | */
62 | class OnBackPressedEvent(private val instance: AppCompatActivity) {
63 |
64 | /** 立即释放返回事件并调用返回功能 */
65 | fun releaseEventAndBack() = instance.callOnBackPressed(ignored = true)
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/factory/BaseAdapterFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/6/3.
22 | */
23 | package com.fankes.coloros.notify.utils.factory
24 |
25 | import android.content.Context
26 | import android.view.LayoutInflater
27 | import android.view.View
28 | import android.view.ViewGroup
29 | import android.widget.BaseAdapter
30 | import android.widget.ListView
31 | import androidx.viewbinding.ViewBinding
32 | import com.highcapable.yukihookapi.hook.factory.method
33 | import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
34 |
35 | /**
36 | * 绑定 [BaseAdapter] 到 [ListView]
37 | * @param initiate 方法体
38 | * @return [BaseAdapter]
39 | */
40 | inline fun ListView.bindAdapter(initiate: BaseAdapterCreater.() -> Unit) =
41 | BaseAdapterCreater(context).apply(initiate).baseAdapter?.apply { adapter = this } ?: error("BaseAdapter not binded")
42 |
43 | /**
44 | * [BaseAdapter] 创建类
45 | * @param context 实例
46 | */
47 | class BaseAdapterCreater(val context: Context) {
48 |
49 | /** 当前 [List] 回调 */
50 | var listDataCallback: (() -> List<*>)? = null
51 |
52 | /** 当前 [BaseAdapter] */
53 | var baseAdapter: BaseAdapter? = null
54 |
55 | /**
56 | * 绑定 [List] 到 [ListView]
57 | * @param result 回调数据
58 | */
59 | fun onBindDatas(result: (() -> List<*>)) {
60 | listDataCallback = result
61 | }
62 |
63 | /**
64 | * 绑定 [BaseAdapter] 到 [ListView]
65 | * @param bindViews 回调 - ([VB] 每项,[Int] 下标)
66 | */
67 | inline fun onBindViews(crossinline bindViews: (binding: VB, position: Int) -> Unit) {
68 | baseAdapter = object : BaseAdapter() {
69 | override fun getCount() = listDataCallback?.let { it() }?.size ?: 0
70 | override fun getItem(position: Int) = listDataCallback?.let { it() }?.get(position)
71 | override fun getItemId(position: Int) = position.toLong()
72 | override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
73 | var holderView = convertView
74 | val holder: VB
75 | if (convertView == null) {
76 | holder = VB::class.java.method {
77 | name = "inflate"
78 | param(LayoutInflaterClass)
79 | }.get().invoke(LayoutInflater.from(context)) ?: error("ViewHolder binding failed")
80 | holderView = holder.root.apply { tag = holder }
81 | } else holder = convertView.tag as VB
82 | bindViews(holder, position)
83 | return holderView ?: error("ViewHolder binding failed")
84 | }
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/factory/DialogBuilderFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/1/7.
22 | */
23 | @file:Suppress("unused")
24 |
25 | package com.fankes.coloros.notify.utils.factory
26 |
27 | import android.app.Dialog
28 | import android.app.TimePickerDialog
29 | import android.content.Context
30 | import android.view.Gravity
31 | import android.view.LayoutInflater
32 | import android.view.View
33 | import android.view.ViewGroup
34 | import android.widget.LinearLayout
35 | import android.widget.TextView
36 | import androidx.appcompat.app.AlertDialog
37 | import androidx.viewbinding.ViewBinding
38 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
39 | import com.google.android.material.progressindicator.CircularProgressIndicator
40 | import com.highcapable.yukihookapi.YukiHookAPI
41 | import com.highcapable.yukihookapi.hook.factory.method
42 | import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
43 |
44 | /**
45 | * 显示时间选择对话框
46 | * @param timeSet 当前时间 - 不写将使用当前时间格式:HH:mm
47 | * @param result 回调 - 小时与分钟 HH:mm
48 | */
49 | fun Context.showTimePicker(timeSet: String = "", result: (String) -> Unit) =
50 | TimePickerDialog(this, { _, h, m -> result("${h.autoZero}:${m.autoZero}") }, timeSet.hour, timeSet.minute, true).show()
51 |
52 | /**
53 | * 构造 [VB] 自定义 View 对话框
54 | * @param initiate 对话框方法体
55 | */
56 | @JvmName(name = "showDialog_Generics")
57 | inline fun Context.showDialog(initiate: DialogBuilder.() -> Unit) =
58 | DialogBuilder(context = this, VB::class.java).apply(initiate).show()
59 |
60 | /**
61 | * 构造对话框
62 | * @param initiate 对话框方法体
63 | */
64 | inline fun Context.showDialog(initiate: DialogBuilder<*>.() -> Unit) = DialogBuilder(context = this).apply(initiate).show()
65 |
66 | /**
67 | * 对话框构造器
68 | * @param context 实例
69 | * @param bindingClass [ViewBinding] 的 [Class] 实例 or null
70 | */
71 | class DialogBuilder(val context: Context, private val bindingClass: Class<*>? = null) {
72 |
73 | /** 实例对象 */
74 | private var instance: AlertDialog.Builder? = null
75 |
76 | /** 对话框取消监听 */
77 | private var onCancel: (() -> Unit)? = null
78 |
79 | /** 对话框实例 */
80 | private var dialogInstance: Dialog? = null
81 |
82 | /** 自定义布局 */
83 | private var customLayoutView: View? = null
84 |
85 | /**
86 | * 获取 [DialogBuilder] 绑定布局对象
87 | * @return [VB]
88 | */
89 | val binding by lazy {
90 | bindingClass?.method {
91 | name = "inflate"
92 | param(LayoutInflaterClass)
93 | }?.get()?.invoke(LayoutInflater.from(context))?.apply {
94 | customLayoutView = root
95 | } ?: error("This dialog maybe not a custom view dialog")
96 | }
97 |
98 | init {
99 | if (YukiHookAPI.Status.isXposedEnvironment) error("This dialog is not allowed to created in Xposed environment")
100 | instance = MaterialAlertDialogBuilder(context)
101 | }
102 |
103 | /** 设置对话框不可关闭 */
104 | fun noCancelable() {
105 | instance?.setCancelable(false)
106 | }
107 |
108 | /** 设置对话框标题 */
109 | var title
110 | get() = ""
111 | set(value) {
112 | instance?.setTitle(value)
113 | }
114 |
115 | /** 设置对话框消息内容 */
116 | var msg
117 | get() = ""
118 | set(value) {
119 | instance?.setMessage(value)
120 | }
121 |
122 | /** 设置进度条对话框消息内容 */
123 | var progressContent
124 | get() = ""
125 | set(value) {
126 | if (customLayoutView == null)
127 | customLayoutView = LinearLayout(context).apply {
128 | orientation = LinearLayout.HORIZONTAL
129 | gravity = Gravity.CENTER or Gravity.START
130 | addView(CircularProgressIndicator(context).apply {
131 | isIndeterminate = true
132 | trackCornerRadius = 10.dp(context)
133 | })
134 | addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
135 | addView(TextView(context).apply {
136 | tag = "progressContent"
137 | text = value
138 | })
139 | setPadding(20.dp(context), 20.dp(context), 20.dp(context), 20.dp(context))
140 | }
141 | else customLayoutView?.findViewWithTag("progressContent")?.text = value
142 | }
143 |
144 | /**
145 | * 设置对话框确定按钮
146 | * @param text 按钮文本内容
147 | * @param callback 点击事件
148 | */
149 | fun confirmButton(text: String = "确定", callback: () -> Unit = {}) {
150 | instance?.setPositiveButton(text) { _, _ -> callback() }
151 | }
152 |
153 | /**
154 | * 设置对话框取消按钮
155 | * @param text 按钮文本内容
156 | * @param callback 点击事件
157 | */
158 | fun cancelButton(text: String = "取消", callback: () -> Unit = {}) {
159 | instance?.setNegativeButton(text) { _, _ -> callback() }
160 | }
161 |
162 | /**
163 | * 设置对话框第三个按钮
164 | * @param text 按钮文本内容
165 | * @param callback 点击事件
166 | */
167 | fun neutralButton(text: String = "更多", callback: () -> Unit = {}) {
168 | instance?.setNeutralButton(text) { _, _ -> callback() }
169 | }
170 |
171 | /**
172 | * 当对话框关闭时
173 | * @param callback 回调
174 | */
175 | fun onCancel(callback: () -> Unit) {
176 | onCancel = callback
177 | }
178 |
179 | /** 取消对话框 */
180 | fun cancel() = dialogInstance?.cancel()
181 |
182 | /** 显示对话框 */
183 | fun show() = runInSafe {
184 | /** 若当前自定义 View 的对话框没有调用 [binding] 将会对其手动调用一次以确保显示布局 */
185 | if (bindingClass != null) binding
186 | instance?.create()?.apply {
187 | customLayoutView?.let { setView(it) }
188 | dialogInstance = this
189 | setOnCancelListener { onCancel?.invoke() }
190 | }?.show()
191 | }
192 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/factory/ExceptionFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/3/13.
22 | */
23 | @file:Suppress("unused")
24 |
25 | package com.fankes.coloros.notify.utils.factory
26 |
27 | import com.highcapable.yukihookapi.hook.log.YLog
28 |
29 | /**
30 | * 忽略异常返回值
31 | * @param result 回调 - 如果异常为空
32 | * @return [T] 发生异常时返回设定值否则返回正常值
33 | */
34 | inline fun safeOfNull(result: () -> T): T? = safeOf(default = null, result)
35 |
36 | /**
37 | * 忽略异常返回值
38 | * @param result 回调 - 如果异常为 false
39 | * @return [Boolean] 发生异常时返回设定值否则返回正常值
40 | */
41 | inline fun safeOfFalse(result: () -> Boolean) = safeOf(default = false, result)
42 |
43 | /**
44 | * 忽略异常返回值
45 | * @param result 回调 - 如果异常为 true
46 | * @return [Boolean] 发生异常时返回设定值否则返回正常值
47 | */
48 | inline fun safeOfTrue(result: () -> Boolean) = safeOf(default = true, result)
49 |
50 | /**
51 | * 忽略异常返回值
52 | * @param result 回调 - 如果异常为 false
53 | * @return [String] 发生异常时返回设定值否则返回正常值
54 | */
55 | inline fun safeOfNothing(result: () -> String) = safeOf(default = "", result)
56 |
57 | /**
58 | * 忽略异常返回值
59 | * @param result 回调 - 如果异常为 false
60 | * @return [Int] 发生异常时返回设定值否则返回正常值
61 | */
62 | inline fun safeOfNan(result: () -> Int) = safeOf(default = 0, result)
63 |
64 | /**
65 | * 忽略异常返回值
66 | * @param default 异常返回值
67 | * @param result 正常回调值
68 | * @return [T] 发生异常时返回设定值否则返回正常值
69 | */
70 | inline fun safeOf(default: T, result: () -> T) = try {
71 | result()
72 | } catch (_: Throwable) {
73 | default
74 | }
75 |
76 | /**
77 | * 忽略异常运行
78 | * @param msg 出错输出的消息 - 默认为空
79 | * @param block 正常回调
80 | */
81 | inline fun T.runInSafe(msg: String = "", block: () -> Unit) {
82 | runCatching(block).onFailure { if (msg.isNotBlank()) YLog.error(msg, it) }
83 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/tool/ActivationPromptTool.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2023/4/17.
22 | */
23 | package com.fankes.coloros.notify.utils.tool
24 |
25 | import android.app.Notification
26 | import android.app.NotificationChannel
27 | import android.app.NotificationManager
28 | import android.app.PendingIntent
29 | import android.content.ComponentName
30 | import android.content.Context
31 | import android.content.Intent
32 | import android.graphics.drawable.Icon
33 | import android.os.Build
34 | import androidx.core.graphics.drawable.toBitmap
35 | import com.fankes.coloros.notify.R
36 | import com.fankes.coloros.notify.utils.factory.appIconOf
37 | import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
38 |
39 | /**
40 | * 模块更新激活提醒通知工具类
41 | */
42 | object ActivationPromptTool {
43 |
44 | /** 当前模块的包名 */
45 | private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID
46 |
47 | /** 推送通知的渠道名称 */
48 | private const val NOTIFY_CHANNEL = "activationPromptId"
49 |
50 | /**
51 | * 推送提醒通知
52 | * @param context 当前实例
53 | * @param packageName 当前 APP 包名
54 | */
55 | fun prompt(context: Context, packageName: String) {
56 | if (packageName != BuildConfigWrapper.APPLICATION_ID) return
57 | context.getSystemService(NotificationManager::class.java)?.apply {
58 | createNotificationChannel(
59 | NotificationChannel(
60 | NOTIFY_CHANNEL, "ColorOS 通知图标增强 - 版本更新",
61 | NotificationManager.IMPORTANCE_DEFAULT
62 | ).apply { enableLights(false) }
63 | )
64 | notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply {
65 | setShowWhen(true)
66 | setContentTitle("模块已更新")
67 | setContentText("点按通知打开模块以完成新版本激活。")
68 | setColor(0xFF4E8A5A.toInt())
69 | setAutoCancel(true)
70 | setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_notify_update))
71 | setLargeIcon(context.appIconOf(packageName)?.toBitmap())
72 | setContentIntent(
73 | PendingIntent.getActivity(
74 | context, packageName.hashCode(),
75 | Intent().apply {
76 | component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.MainActivity")
77 | flags = Intent.FLAG_ACTIVITY_NEW_TASK
78 | }, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
79 | )
80 | )
81 | }.build())
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/tool/BitmapCompatTool.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2023/1/28.
22 | */
23 | package com.fankes.coloros.notify.utils.tool
24 |
25 | import android.graphics.Bitmap
26 | import android.graphics.Canvas
27 | import android.graphics.Matrix
28 | import android.graphics.Paint
29 | import android.graphics.PorterDuff
30 | import android.graphics.drawable.AnimationDrawable
31 | import android.graphics.drawable.BitmapDrawable
32 | import android.graphics.drawable.Drawable
33 | import android.graphics.drawable.VectorDrawable
34 | import android.util.ArrayMap
35 | import androidx.core.graphics.drawable.toBitmap
36 | import com.fankes.coloros.notify.utils.factory.safeOfFalse
37 | import kotlin.math.abs
38 |
39 | /**
40 | * 这是一个从 AOSP 源码中分离出来的功能
41 | *
42 | * 主要作用于兼容部分第三方系统修改颜色判断代码造成判断位图灰度功能失效
43 | */
44 | object BitmapCompatTool {
45 |
46 | /** 缓存已判断的结果防止卡顿 */
47 | private var cachedBitmapGrayscales = ArrayMap()
48 |
49 | private var tempBuffer = intArrayOf(0)
50 | private var tempCompactBitmap: Bitmap? = null
51 | private var tempCompactBitmapCanvas: Canvas? = null
52 | private var tempCompactBitmapPaint: Paint? = null
53 | private val tempMatrix = Matrix()
54 |
55 | /**
56 | * 判断 [Drawable] 是否为灰度位图
57 | * @param drawable 要判断的 [Drawable]
58 | * @return [Boolean] 是否灰度
59 | */
60 | fun isGrayscaleDrawable(drawable: Drawable) = safeOfFalse {
61 | when (drawable) {
62 | is BitmapDrawable -> isGrayscaleBitmap(drawable.bitmap)
63 | is AnimationDrawable -> !(drawable.numberOfFrames <= 0 || !isGrayscaleBitmap(drawable.getFrame(0).toBitmap()))
64 | is VectorDrawable -> true
65 | else -> isGrayscaleBitmap(drawable.toBitmap())
66 | }
67 | }
68 |
69 | /**
70 | * 判断 [Bitmap] 是否为灰度位图
71 | * @param bitmap 要判断的位图
72 | * @return [Boolean] 是否灰度
73 | */
74 | private fun isGrayscaleBitmap(bitmap: Bitmap) =
75 | cachedBitmapGrayscales[bitmap.generationId] ?: let {
76 | var height = bitmap.height
77 | var width = bitmap.width
78 | if (height > 64 || width > 64) {
79 | if (tempCompactBitmap == null) {
80 | tempCompactBitmap = Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888)
81 | .also { tempCompactBitmapCanvas = Canvas(it) }
82 | tempCompactBitmapPaint = Paint(Paint.FILTER_BITMAP_FLAG).apply { isFilterBitmap = true }
83 | }
84 | tempMatrix.reset()
85 | tempMatrix.setScale(64f / width, 64f / height, 0f, 0f)
86 | tempCompactBitmapCanvas?.drawColor(0, PorterDuff.Mode.SRC)
87 | tempCompactBitmapCanvas?.drawBitmap(bitmap, tempMatrix, tempCompactBitmapPaint)
88 | height = 64
89 | width = 64
90 | }
91 | val size = height * width
92 | ensureBufferSize(size)
93 | tempCompactBitmap?.getPixels(tempBuffer, 0, width, 0, 0, width, height)
94 | for (i in 0 until size)
95 | if (isGrayscaleColor(tempBuffer[i]).not()) {
96 | cachedBitmapGrayscales[bitmap.generationId] = false
97 | return@let false
98 | }
99 | cachedBitmapGrayscales[bitmap.generationId] = true
100 | true
101 | }
102 |
103 | /**
104 | * 提纯 [Bitmap] 颜色判断灰度
105 | * @param color 颜色
106 | * @return [Boolean] 是否灰度
107 | */
108 | private fun isGrayscaleColor(color: Int): Boolean {
109 | if (color shr 24 and 255 < 50) return true
110 | val r = color shr 16 and 255
111 | val g = color shr 8 and 255
112 | val b = color and 255
113 | return !(abs(r - g) >= 20 || abs(r - b) >= 20 || abs(g - b) >= 20)
114 | }
115 |
116 | /**
117 | * 计算字节数组
118 | * @param size 大小
119 | */
120 | private fun ensureBufferSize(size: Int) {
121 | if (tempBuffer.size < size) tempBuffer = IntArray(size)
122 | }
123 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/tool/GithubReleaseTool.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/3/20.
22 | */
23 | package com.fankes.coloros.notify.utils.tool
24 |
25 | import android.app.Activity
26 | import android.content.Context
27 | import android.icu.text.SimpleDateFormat
28 | import android.icu.util.Calendar
29 | import android.icu.util.TimeZone
30 | import com.fankes.coloros.notify.utils.factory.isNetWorkSuccess
31 | import com.fankes.coloros.notify.utils.factory.openBrowser
32 | import com.fankes.coloros.notify.utils.factory.openSelfSetting
33 | import com.fankes.coloros.notify.utils.factory.runInSafe
34 | import com.fankes.coloros.notify.utils.factory.showDialog
35 | import okhttp3.Call
36 | import okhttp3.Callback
37 | import okhttp3.OkHttpClient
38 | import okhttp3.Request
39 | import okhttp3.Response
40 | import org.json.JSONObject
41 | import java.io.IOException
42 | import java.io.Serializable
43 | import java.util.Locale
44 |
45 | /**
46 | * 获取 GitHub Release 最新版本工具类
47 | */
48 | object GithubReleaseTool {
49 |
50 | /** 仓库作者 */
51 | private const val REPO_AUTHOR = "fankes"
52 |
53 | /** 仓库名称 */
54 | private const val REPO_NAME = "ColorOSNotifyIcon"
55 |
56 | /**
57 | * 获取最新版本信息
58 | * @param context 实例
59 | * @param version 当前版本
60 | * @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
61 | */
62 | fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
63 | OkHttpClient().newBuilder().build().newCall(
64 | Request.Builder()
65 | .url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest")
66 | .get()
67 | .build()
68 | ).enqueue(object : Callback {
69 | override fun onFailure(call: Call, e: IOException) {}
70 |
71 | override fun onResponse(call: Call, response: Response) = runInSafe {
72 | JSONObject(response.body.string()).apply {
73 | GithubReleaseBean(
74 | name = getString("name"),
75 | htmlUrl = getString("html_url"),
76 | content = getString("body"),
77 | date = getString("published_at").localTime()
78 | ).apply {
79 | fun showUpdate() = context.showDialog {
80 | title = "最新版本 $name"
81 | msg = "发布于 $date\n\n" +
82 | "更新日志\n\n" + content
83 | confirmButton(text = "更新") { context.openBrowser(htmlUrl) }
84 | cancelButton()
85 | }
86 | if (name != version) (context as? Activity?)?.runOnUiThread {
87 | showUpdate()
88 | result(name) { showUpdate() }
89 | }
90 | }
91 | }
92 | }
93 | })
94 | }
95 |
96 | /**
97 | * 检查网络连接情况
98 | * @param context 实例
99 | * @param result 已连接回调
100 | */
101 | private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe {
102 | if (context.isNetWorkSuccess)
103 | OkHttpClient().newBuilder().build().newCall(
104 | Request.Builder()
105 | .url("https://www.baidu.com")
106 | .get()
107 | .build()
108 | ).enqueue(object : Callback {
109 | override fun onFailure(call: Call, e: IOException) {
110 | (context as? Activity?)?.runOnUiThread {
111 | context.showDialog {
112 | title = "网络不可用"
113 | msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
114 | confirmButton(text = "去开启") { context.openSelfSetting() }
115 | cancelButton()
116 | noCancelable()
117 | }
118 | }
119 | }
120 |
121 | override fun onResponse(call: Call, response: Response) = runInSafe {
122 | (context as? Activity?)?.runOnUiThread { runInSafe { result() } }
123 | }
124 | })
125 | }
126 |
127 | /**
128 | * 格式化时间为本地时区
129 | * @return [String] 本地时区时间
130 | */
131 | private fun String.localTime() = replace("T", " ").replace("Z", "").let {
132 | runCatching {
133 | val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
134 | val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
135 | local.format(current.parse(it))
136 | }.getOrNull() ?: it
137 | }
138 |
139 | /**
140 | * GitHub Release bean
141 | * @param name 版本名称
142 | * @param htmlUrl 网页地址
143 | * @param content 更新日志
144 | * @param date 发布时间
145 | */
146 | private data class GithubReleaseBean(
147 | var name: String,
148 | var htmlUrl: String,
149 | var content: String,
150 | var date: String
151 | ) : Serializable
152 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/tool/I18nWarnTool.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2023/2/3.
22 | */
23 | package com.fankes.coloros.notify.utils.tool
24 |
25 | import android.content.Context
26 | import com.fankes.coloros.notify.utils.factory.showDialog
27 | import com.highcapable.yukihookapi.hook.factory.prefs
28 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
29 | import java.util.Locale
30 |
31 | /**
32 | * I18n 适配警告提示工具类
33 | */
34 | object I18nWarnTool {
35 |
36 | /** 推广已读存储键值 */
37 | private val LOCALE_WARN_READED = PrefsData("locale_warn_readed", false)
38 |
39 | /**
40 | * 检查并显示 I18n 适配警告对话框
41 | * @param context 实例
42 | */
43 | fun checkingOrShowing(context: Context) {
44 | fun saveReaded() = context.prefs().edit { put(LOCALE_WARN_READED, value = true) }
45 | if (Locale.getDefault().language.startsWith("zh").not() && context.prefs().get(LOCALE_WARN_READED).not())
46 | context.showDialog {
47 | title = "Notice of I18n Support"
48 | msg = "This Xposed Module is only for Chinese and the Chinese region.\n\n" +
49 | "Currently, there will be no internationalization adaptation.\n\n" +
50 | "There may be plans for internationalization adaptation in the future, so stay tuned."
51 | confirmButton(text = "Got It") { saveReaded() }
52 | noCancelable()
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/tool/IconAdaptationTool.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *
11 | *
12 | * This software is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * and eula along with this software. If not, see
19 | *
20 | *
21 | * This file is created by fankes on 2022/3/20.
22 | */
23 | package com.fankes.coloros.notify.utils.tool
24 |
25 | import android.app.Notification
26 | import android.app.NotificationChannel
27 | import android.app.NotificationManager
28 | import android.app.PendingIntent
29 | import android.content.ComponentName
30 | import android.content.Context
31 | import android.content.Intent
32 | import android.graphics.drawable.Icon
33 | import android.os.Build
34 | import androidx.core.graphics.drawable.toBitmap
35 | import com.fankes.coloros.notify.R
36 | import com.fankes.coloros.notify.hook.HookEntry
37 | import com.fankes.coloros.notify.utils.factory.appIconOf
38 | import com.fankes.coloros.notify.utils.factory.appNameOf
39 | import com.fankes.coloros.notify.utils.factory.isDebugApp
40 | import com.fankes.coloros.notify.utils.factory.isSystemApp
41 | import com.fankes.coloros.notify.utils.factory.runInSafe
42 | import com.fankes.coloros.notify.utils.factory.stampToDate
43 | import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
44 |
45 | /**
46 | * 通知图标适配推送通知类
47 | *
48 | * 这个类需要在 [HookEntry] 中调用
49 | */
50 | object IconAdaptationTool {
51 |
52 | /** 当前模块的包名 */
53 | private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID
54 |
55 | /** 推送通知的渠道名称 */
56 | private const val NOTIFY_CHANNEL = "notifyRuleSupportId"
57 |
58 | /** 已过期的时间 */
59 | private val outTimeLimits = HashSet()
60 |
61 | /**
62 | * 推送新 APP 安装适配通知
63 | * @param context 实例
64 | * @param packageName 安装的 APP 包名
65 | */
66 | fun pushNewAppSupportNotify(context: Context, packageName: String) {
67 | if (packageName.startsWith("com.google.android.trichromelibrary")) return
68 | if (context.isSystemApp(packageName) || context.isDebugApp(packageName)) return
69 | context.getSystemService(NotificationManager::class.java)?.apply {
70 | createNotificationChannel(
71 | NotificationChannel(
72 | NOTIFY_CHANNEL, "通知图标优化适配",
73 | NotificationManager.IMPORTANCE_DEFAULT
74 | ).apply { enableLights(false) }
75 | )
76 | notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply {
77 | setShowWhen(true)
78 | setContentTitle("您已安装 ${context.appNameOf(packageName)}")
79 | setContentText("尚未适配此应用,点按打开在线规则。")
80 | setColor(0xFF2993F0.toInt())
81 | setAutoCancel(true)
82 | setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_unsupported))
83 | setLargeIcon(context.appIconOf(packageName)?.toBitmap())
84 | setContentIntent(
85 | PendingIntent.getActivity(
86 | context, packageName.hashCode(),
87 | Intent().apply {
88 | component = ComponentName(
89 | MODULE_PACKAGE_NAME,
90 | "$MODULE_PACKAGE_NAME.ui.activity.ConfigureActivity"
91 | )
92 | flags = Intent.FLAG_ACTIVITY_NEW_TASK
93 | }.apply {
94 | putExtra("isNewAppSupport", true)
95 | putExtra("appName", context.appNameOf(packageName))
96 | putExtra("pkgName", packageName)
97 | }, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
98 | )
99 | )
100 | }.build())
101 | }
102 | }
103 |
104 | /**
105 | * 检测 APP 卸载后移除相关通知
106 | * @param context 实例
107 | * @param packageName 卸载的 APP 包名
108 | */
109 | fun removeNewAppSupportNotify(context: Context, packageName: String) = runInSafe {
110 | context.getSystemService(NotificationManager::class.java)?.cancel(packageName.hashCode())
111 | }
112 |
113 | /**
114 | * 自动更新通知图标优化在线规则
115 | * @param context 实例
116 | * @param timeSet 设定的时间
117 | */
118 | fun prepareAutoUpdateIconRule(context: Context, timeSet: String) = runInSafe {
119 | System.currentTimeMillis().also {
120 | val nowTime = it.stampToDate(format = "HH:mm")
121 | if (timeSet != nowTime || outTimeLimits.any { e -> e == nowTime }) return
122 | outTimeLimits.add(nowTime)
123 | context.startActivity(
124 | Intent().apply {
125 | component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.auto.NotifyIconRuleUpdateActivity")
126 | flags = Intent.FLAG_ACTIVITY_NEW_TASK
127 | }
128 | )
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fankes/coloros/notify/utils/tool/SystemUITool.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
4 | * https://github.com/fankes/ColorOSNotifyIcon
5 | *
6 | * This software is non-free but opensource software: you can redistribute it
7 | * and/or modify it under the terms of the GNU Affero General Public License
8 | * as published by the Free Software Foundation; either
9 | * version 3 of the License, or any later version.
10 | *