212 Commits

Author SHA1 Message Date
570e5a23ba chore: update target sdk to 36 2025-08-19 17:02:41 +08:00
90aec5b9fa chore: update jdk to 21 2025-08-19 17:02:33 +08:00
7f3cdba757 chore: bump gradle to 8.14.3 2025-08-19 17:02:22 +08:00
9536b42c2b chore: bump dependencies 2025-08-19 17:02:00 +08:00
a7582de004 refactor: update KavaRef usage to 1.0.1 2025-07-06 21:37:37 +08:00
29b14a811e refactor: migrate and update to YukiHookAPI 1.3.0 2025-06-25 23:22:49 +08:00
9b9eda8a39 docs: update README 2025-06-24 14:11:25 +08:00
cfa065b57e docs: update README 2025-06-20 12:52:18 +08:00
YuSaki丶Kanade
492d0dad67 fix: block coloros 15 fixSmallIcon method (#78)
* fix: block coloros 15 fixSmallIcon method

* fix: hook error

* feat: complete ColorOS 15 support

* docs: update README

---------

Co-authored-by: fankesyooni <qzmmcn@163.com>
2025-05-29 22:12:27 +08:00
YuSaki丶Kanade
d26f68c1a7 fix: DND and updateIconsForLayout hook (#79) 2025-05-28 22:50:39 +08:00
d9520cb484 chore: disable type auto conversion for sweet-property 2025-05-09 23:20:08 +08:00
884aa87617 chore: update project files 2025-05-09 23:20:06 +08:00
YuSaki丶Kanade
51e52f3eba fix: block coloros 15 useAppIconForSmallIcon method (#77) 2025-04-22 02:15:08 +08:00
YuSaki丶Kanade
9aae48c785 fix: coloros 15 removed updateIconsForLayout method (#76) 2025-04-21 22:40:21 +08:00
ee77842fbb chore: bump dependencies 2025-03-16 23:29:32 +08:00
3794035789 chore: bump gradle to 8.13 2025-03-16 23:29:26 +08:00
a8c1ba3166 chore: bump ci to v4 2025-02-22 01:10:00 +08:00
4d39eb4b9d chore: update .gitignore 2025-02-22 01:09:50 +08:00
2ffd36cc21 docs: update license 2025-01-13 11:17:22 +08:00
d95e53f6e4 refactor: support Android 15 edge-to-edge system bars 2024-11-25 18:50:23 +08:00
115ccf0822 chore: update target sdk to 35 2024-11-10 21:36:17 +08:00
982f73444a chore: some tweaks in build.gradle.kts 2024-11-10 21:35:12 +08:00
93fdd307a2 chore: update project files 2024-11-10 21:35:09 +08:00
75789c0286 chore: bump dependencies 2024-11-10 21:35:02 +08:00
b1ca7be6ed chore: bump gradle to 8.10.2 2024-11-10 21:06:44 +08:00
c4087db1e6 docs: add Android 15 option 2024-10-10 09:09:42 +08:00
hamjin
bf69aed1b4 feat: Replace ghproxy.com with ghp.ci (#69) 2024-10-03 15:48:55 +08:00
a0c001b7e4 Revert "feat: Custom GitHub Proxy (#68)"
This reverts commit 1b0fcdfce6.
2024-10-03 01:27:38 +08:00
hamjin
1b0fcdfce6 feat: Custom GitHub Proxy (#68)
* feat: Custom GitHub Proxy

Signed-off-by: hamjin <jinham@qq.com>

* refactor: organize text layout

---------

Signed-off-by: hamjin <jinham@qq.com>
Co-authored-by: Fankesyooni <37344460+fankes@users.noreply.github.com>
2024-10-03 00:29:03 +08:00
8d42d47ab4 chore: update .editorconfig 2024-06-21 10:11:02 +08:00
a7c43149e3 chore: update project files 2024-06-21 10:10:54 +08:00
053c8b837c refactor: support Kotlin 2.0.0 2024-06-20 11:26:22 +08:00
1fdaf81560 chore: bump dependencies 2024-06-20 11:25:22 +08:00
67937edd0f chore: bump gradle to 8.7 2024-06-20 11:25:13 +08:00
bb9e965cca chore: update .editorconfig 2024-06-20 11:25:08 +08:00
d7e487c4ce chore: bump dependencies 2024-02-20 11:39:43 +08:00
11480f6d4a chore: bump gradle to 8.6 2024-02-20 11:39:39 +08:00
a09d039b48 chore: update .editorconfig 2024-01-13 21:25:46 +08:00
95b49168cb docs: update copyright date to 2024 for all existing files 2024-01-01 01:23:24 +08:00
49b942efda style: merge to new ktlint version & rules 2023-12-29 21:39:05 +08:00
025183d8f0 chore: update project files 2023-12-29 21:39:00 +08:00
78205b2c6c chore: bump dependencies 2023-12-28 23:55:23 +08:00
c3ac4d4be1 chore: bump gradle to 8.5 2023-12-28 23:53:40 +08:00
a22d36c79d chore: update .gitignore 2023-12-28 23:53:29 +08:00
8889d6fa5f docs: use relative link 2023-12-11 02:39:42 +08:00
8662637e6a chore: update project files 2023-12-11 02:39:39 +08:00
b88b7b6907 chore: bump dependency versions 2023-12-11 00:27:21 +08:00
ae0f163d57 docs: update piracy statement 2023-12-11 00:27:18 +08:00
757313ee14 style: optimize code 2023-12-11 00:26:17 +08:00
Fankesyooni
0ee83ff83a Merge pull request #65 from hamjin/master
Fix: ColorOS 14 new hook points
2023-12-03 12:35:12 +08:00
hamjin
29c3a839f4 Bugfix for ColorOS 14
Tested On OnePlus PHK110 14.0.0.200 with com.android.systemui 14.99.09

Signed-off-by: hamjin <jinham@qq.com>
2023-12-03 11:49:12 +08:00
faf443960c docs: update promotion 2023-11-18 18:16:50 +08:00
41cf6a209a chore: bump "com.highcapable.sweetdependency" version to 1.0.4 2023-11-14 01:02:38 +08:00
71a37d8bf3 chore: bump "com.highcapable.sweetproperty" version to 1.0.5 2023-11-08 15:20:21 +08:00
4afddae4e1 chore: updaye project files 2023-11-08 15:17:45 +08:00
3714c3f06d chore: bump dependencies 2023-11-04 03:52:02 +08:00
6d624b3992 chore: bump plugin versions
- bump "com.highcapable.sweetdependency" version to 1.0.3
- bump "com.highcapable.sweetproperty" version to 1.0.4
2023-11-04 03:48:54 +08:00
b3a3e0993c docs: replace download links 2023-11-03 13:56:46 +08:00
c34f121145 docs: update release channel 2023-10-26 21:30:06 +08:00
b2a65a4f3c fix: catch toast when no looper 2023-10-22 22:38:16 +08:00
799d758671 chore: update target sdk to 34 2023-10-21 01:22:33 +08:00
9f323f466a feat(docs): update YukiHookAPI owner link 2023-10-21 01:22:13 +08:00
713d5bc008 refactor: remove DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION 2023-10-21 01:21:31 +08:00
e0f50984b0 refactor: migrate to YukiHookAPI new usage 2023-10-07 21:07:40 +08:00
1e0efe8f86 chore: bump dependency versions 2023-10-07 21:07:32 +08:00
358fc16b0e chore: bump plugin versions
- bump "com.highcapable.sweetdependency" version to 1.0.2
- bump "com.highcapable.sweetproperty" version to 1.0.3
2023-09-26 09:08:58 +08:00
fc69d7b544 refactor: add new R8 rules to fix possible problems 2023-09-19 08:18:46 +08:00
5e37c1b7dc ci: fix artifacts name 2023-09-19 01:52:17 +08:00
147a33d8ee feat: simple support Android 14 display name 2023-09-18 00:21:14 +08:00
75ec1317fa docs: optimize comments 2023-09-18 00:19:06 +08:00
62d4e727ff feat: lots of changes
- add BuildConfigWrapper
- merge to new project promote
- add ci version tag support
- Fix system api compat issues
2023-09-18 00:17:25 +08:00
c4573d221b docs: update README 2023-09-18 00:16:01 +08:00
b74adce241 style: optimize code 2023-09-18 00:15:13 +08:00
bd57f76232 docs: move images to img-src 2023-09-18 00:13:19 +08:00
e67110fd65 refactor: use new payment code 2023-09-18 00:12:18 +08:00
2c48401d21 fix: class not found when R8 since android gradle plugin 8+ 2023-09-18 00:11:32 +08:00
bcac9a3752 ci: optimize and add artifacts post to Telegram 2023-09-18 00:11:07 +08:00
656fc0446c chore: add Android 14 option 2023-09-18 00:10:37 +08:00
b4f0b65264 chore: migrate build script from groovy to kts
- using SweetDependency, SweetProperty
- merge singing key file configs to properties
- update gradle and dependencies
2023-09-18 00:10:08 +08:00
a577c49e56 chore: clean up build step files 2023-09-18 00:08:23 +08:00
1431457e37 [Change Commit Specification] Use the new commit spec from here on
child commits:
chore: add .editorconfig
2023-09-18 00:05:38 +08:00
4edf7a2df2 Modify support ColorOS 13.1 notify panel alpha in MainActivity 2023-07-15 13:55:05 +08:00
171bbae16b Update version to 1.100 2023-04-25 06:40:35 +08:00
c1d6a85927 Modify merge to YukiHookAPI new usage 2023-04-25 06:23:59 +08:00
ea77b3631f Update YukiHookAPI 2023-04-25 06:22:05 +08:00
8b906032f2 Update version to 1.99 2023-04-22 22:37:13 +08:00
6cd9cb79ac Modify remove prefs cache function and disable prefs cache 2023-04-22 22:32:35 +08:00
8bdff8d558 Update version to 1.98 2023-04-21 02:22:05 +08:00
3ccde65af6 Added enable or disable prefs cache function 2023-04-21 02:15:11 +08:00
7503a64efd Update YukiHookAPI 2023-04-21 01:29:55 +08:00
0d564258c9 Update version to 1.97 2023-04-18 01:51:35 +08:00
f172b6b1b9 Fix notification panel background tint list color not set problem in SystemUIHooker 2023-04-18 01:46:17 +08:00
ecf7ed8653 Modify change some descriptions in activity_main 2023-04-18 01:07:05 +08:00
1efb18f4e1 Fix notification panel icon reset by system problem in SystemUIHooker 2023-04-18 01:05:35 +08:00
fa99a57027 Added new GitHub proxy url 2023-04-18 00:32:05 +08:00
4d3106f5bc Added new module version update notification function 2023-04-17 23:46:55 +08:00
8f735ab23d Fix default notification panel background tint color changed on some custom ColorOS in SystemUIHooker 2023-04-17 23:07:05 +08:00
3503cf35b1 Added notification icon force system color function 2023-04-17 22:42:55 +08:00
1d5759313b Modify merge to YukiHookAPI new usage 2023-04-17 06:13:35 +08:00
7dcd6de33e Update YukiHookAPI 2023-04-17 06:09:35 +08:00
ff941171f9 Modify merge contents of build.gradle into constant definitions 2023-04-15 23:16:55 +08:00
eeb7966448 Update Gradle & Kotlin
- Update Kotlin version to 1.8.20
- Update Gradle version to 8.0.2
- Update Gradle dependencies
2023-04-08 00:09:56 +08:00
a1214f0f38 Modify support monet launcher icon in ic_launcher 2023-02-25 21:25:15 +08:00
855f88127d Update version to 1.95 2023-02-07 06:37:15 +08:00
d589ef2cd0 Modify change wallpaperColor function's return value that cannot obtain return default color in FunctionFactory 2023-02-07 06:23:35 +08:00
c95c36c76a Modify optimize code in SystemUIHooker 2023-02-07 06:19:55 +08:00
2eec2022d1 Fix "GitHub" spelling in all files 2023-02-07 05:25:55 +08:00
4b00d84d8b Modify replace to new placeholder notification icon in SystemUIHooker 2023-02-07 05:21:15 +08:00
89767143bd Fix possible grayscale notification icons color judgment problem in SystemUIHooker 2023-02-07 03:55:55 +08:00
e119fd0bf7 Fix VariousClass's names misspelling problem in SystemUIHooker 2023-02-07 03:39:05 +08:00
bd08b2ce1d Fix notification panel maybe freeze problem in SystemUIHooker
- Remove replacement notification icon views function
- Use a new way to fix notification icons color
2023-02-07 03:27:23 +08:00
a334bf9a8c Modify hide notification panel alpha function when ColorOS version without 12, 12.1, 13 in MainActivity, ConfigData, activity_main 2023-02-07 02:23:27 +08:00
bad773cc2c Fix some class not found problem on ColorOS 11 in SystemUIHooker 2023-02-07 02:20:15 +08:00
e5a2edb614 Update version to 1.9 2023-02-06 04:09:35 +08:00
b9a8b6195b Update Gradle & PlatformSDK
- Update Android Gradle Plugin version to 7.4.1
2023-02-06 03:57:55 +08:00
0cc499e690 Modify merge to new readme documentation 2023-02-06 03:00:55 +08:00
df4694bb56 Added automatic build workflows for Github Actions 2023-02-06 02:59:35 +08:00
c0dbf565e9 Update copyright date to 2023 for all existing files 2023-02-06 02:29:15 +08:00
2f0766cc9f Modify change some descriptions in activity_main 2023-02-06 02:23:35 +08:00
896322361e Modify merge to new notification icon rules urls in IconRuleManagerTool, ConfigureActivity, dia_source_from 2023-02-06 02:17:35 +08:00
9e53e0ddba Fix the custom url sync result callback maybe not canceled the dialog problem in IconRuleManagerTool 2023-02-06 01:59:35 +08:00
41b9127c15 Modify remove extra text in notifyRefresh function in IconRuleManagerTool 2023-02-06 01:51:55 +08:00
24a028629d Modify swap variable name places in IconRuleManagerTool 2023-02-06 01:19:15 +08:00
70c2f9aa8f Modify change module debug logs record method and optimize some code in HookEntry, SystemUIHooker, SystemUITool, MainActivity, activity_main 2023-02-05 04:51:35 +08:00
4ad7acb56c Added color notification icons placeholder icon function in SystemUIHooker, ConfigData, MainActivity, activity_main 2023-02-05 02:32:35 +08:00
bae57c87d8 Modify optimize code in SystemUIHooker 2023-02-04 23:30:15 +08:00
7938477bb3 Modify change when module is disabled turn off some functions in ConfigureActivity, SystemUITool 2023-02-04 22:42:35 +08:00
1253b8c371 Modify merge seek bar changed event to SeekBarFactory 2023-02-04 02:43:39 +08:00
dea19c72ab Added retry notification function when notification icon rule sync failed in ConfigureActivity, IconRuleManagerTool 2023-02-03 23:59:15 +08:00
fbce4f9f7a Modify change ProgressBar to CircularProgressIndicator in DialogBuilderFactory 2023-02-03 23:20:55 +08:00
3697a9ad18 Added I18nWarnTool in MainActivity 2023-02-03 05:37:15 +08:00
aaca48bb8e Added Android version code name in system version content in MainActivity 2023-02-03 05:13:55 +08:00
28e579ad0e Added androidVersionCodeName function in FunctionFactory 2023-02-03 05:11:55 +08:00
b8973fa15a Added dynamic refreshing option in restart system ui dialog in SystemUITool 2023-02-03 04:50:35 +08:00
3fa8a3c7e9 Modify merge all store methods to ConfigData and fix module activation status problem 2023-02-03 04:19:15 +08:00
cc2e2df37e Modify merge issues url to AndroidNotifyIconAdapt in ConfigureActivity 2023-02-02 22:03:15 +08:00
6725bf14a5 Modify merge notification icon panel functions and remove from notification icon fix panel in SystemUIHooker, MainActivity, activity_main 2023-02-02 05:59:35 +08:00
a896806fa5 Modify change Android 12 notification icon style contents to Material 3 in DataConst, SystemUIHooker, MainActivity, activity_main 2023-02-02 05:05:55 +08:00
5e56c1bc57 Fix some system's notification icons maybe reset by the system cause wrong icon color in SystemUIHooker 2023-02-02 00:09:15 +08:00
3c96c30a74 Modify change sleep millisecond cause some system not refresh caching problem in SystemUIHooker 2023-02-01 06:27:15 +08:00
ca4a594114 Fix auto update notify icon rules problem in IconAdaptationTool, IconRuleManagerTool, SystemUITool, NotifyIconRuleUpdateActivity 2023-02-01 06:25:05 +08:00
77ff08f6d7 Modify merge to YukiHookAPI new usage 2023-02-01 05:02:15 +08:00
4fd017fed1 Update YukiHookAPI 2023-02-01 04:50:50 +08:00
91b1500ebd Update .gitignore 2023-02-01 04:50:06 +08:00
601ceee89f Modify change BuildConfig.APPLICATION_ID to MODULE_PACKAGE_NAME in IconAdaptationTool 2023-01-29 02:11:50 +08:00
e33c51a320 Modify inject module resources to system ui in SystemUIHooker
- Merge app support notification icon type to vector
- Remove all android adb hardcode base64 icon data
- Merge android default icon to "com.android.internal.R.drawable.stat_sys_adb"
2023-01-29 02:05:15 +08:00
9c8df9f173 Modify change package id in build.gradle 2023-01-29 00:56:57 +08:00
1e98c0d431 Modify move new app support functions to a new way to implement and shielded some apps that shouldn't appear in SystemUIHooker 2023-01-29 00:45:35 +08:00
8ef9e206a6 Added isSystemApp function in FunctionFactory 2023-01-29 00:25:40 +08:00
ca5da08bc2 Modify change isAppDebuggable function name to isDebugApp in FunctionFactory 2023-01-29 00:25:17 +08:00
56da358999 Update README.md 2023-01-28 01:57:06 +08:00
53e5292f2e Added notify icon color compat functions and fix fatal error when some bitmap calculations are abnormal 2023-01-28 01:53:55 +08:00
1b4be321ec Modify add friendly root permission access fail tips in SystemUITool 2023-01-28 00:16:32 +08:00
ddcc475ea1 Modify merge all png elements to svg elements 2023-01-27 15:55:35 +08:00
65142c3af7 Modify replace sync url FastGit to 7ED Services in IconRuleManagerTool, dia_source_from 2023-01-27 02:55:25 +08:00
118104c11c Modify change unsupported note in MainActivity 2023-01-27 02:45:23 +08:00
1fd9cc6ab0 Added Android 13 icon in IconPackParams, SystemUIHooker 2023-01-27 01:59:20 +08:00
889092dd3d Modify remove some method's param name statement 2023-01-27 01:03:09 +08:00
9d695c61f0 Modify merge to YukiHookAPI new usage 2023-01-27 00:59:50 +08:00
81a6ada0ee Modify rename ui/view to ui/widget 2023-01-27 00:09:15 +08:00
5fb4e0ac39 Modify change debug log tag "ColorOSNotify" to "ColorOSNotifyIcon" in HookEntry 2023-01-27 00:01:25 +08:00
62a7d87502 Modify optimize code format in MainActivity 2023-01-26 23:59:17 +08:00
95a9e3253b Update Gradle & PlatformSDK
- Update Android Gradle Plugin version to 7.4.0
- Update Kotlin version to 1.7.22
- Update Gradle dependencies
2023-01-26 23:55:11 +08:00
bff047aa48 Update YukiHookAPI 2023-01-26 23:51:22 +08:00
975ebe7e49 Modify change image url to raw in README 2023-01-26 23:49:49 +08:00
eb9c93a2f5 Fix the central color problem of views such as CheckBox 2023-01-16 22:43:43 +08:00
ff43641d63 Modify remove localTime function time second format and change description text in GithubReleaseTool 2023-01-14 00:21:22 +08:00
22f5ebc625 Modify change promote message in YukiPromoteTool 2023-01-14 00:20:55 +08:00
2f302a3c10 Update Gradle & PlatformSDK
- Update Android Gradle Plugin version to 7.3.1
- Update Kotlin version to 1.7.20
2022-10-20 00:16:42 +08:00
fa2aa675d0 Modify change hideOrShowLauncherIcon in a hard-code way 2022-10-06 03:28:07 +08:00
eb090b11de Modify merge to new DialogBuilderFactory 2022-10-06 03:21:02 +08:00
97161f8692 Modify merge YukiHookAPI new usage and compatible with API 33 2022-10-06 03:19:09 +08:00
6159308cbb Added POST_NOTIFICATIONS permission in AndroidManifest 2022-10-06 01:37:55 +08:00
be6447342f Added Project icon 2022-10-06 01:36:48 +08:00
c0e9ed4673 Update Gradle & PlatformSDK 2022-10-06 01:06:08 +08:00
45328bd1a1 Update YukiHookAPI 2022-10-06 01:05:00 +08:00
Fankesyooni
27fdcfbae7 Added new bug report issues template 2022-10-06 00:54:35 +08:00
Fankesyooni
c021b986f5 Delete ----bug---.md 2022-10-06 00:54:00 +08:00
Fankesyooni
2b5f3e5f88 Added new notify icon adaption issues template 2022-10-06 00:44:56 +08:00
Fankesyooni
30bdb0582f Delete ----------.md 2022-10-06 00:44:23 +08:00
e4b006afc0 Update Gradle & Kotlin & PlatformSDK
- Update Kotlin version to 1.7.10
- Update Gradle version
2022-07-20 23:17:53 +08:00
a21b1382a4 Merge dependencies 2022-07-20 02:12:32 +08:00
3e3127adcf Update Gradle & Kotlin & PlatformSDK
- Update Kotlin version to 1.7.0
- Update Gradle dependencies
- Merge legacy code
2022-06-10 17:25:08 +08:00
6aea7ca11f Merge code 2022-06-08 19:11:05 +08:00
6b9e9b5843 Make UI to Primary Theme 2022-06-08 15:14:22 +08:00
5747fa91ca Merge DialogBuilderFactory with new code style 2022-06-07 16:56:24 +08:00
1a73613cd1 Fix GithubReleaseTool to LocalTime 2022-06-04 02:59:05 +08:00
4ee61adb07 Added BuildConfig.VERSION_NAME changed 2022-06-03 23:54:53 +08:00
80b402ee40 Merge ListView's adapter to BaseAdapterFactory 2022-06-03 01:56:11 +08:00
c02bc2314a Update version to 1.85 2022-05-31 04:06:23 +08:00
d177620784 Added notify icon custom corner function 2022-05-31 03:43:46 +08:00
354d0e1961 Update misc 2022-05-31 03:43:42 +08:00
bc1f379650 Update YukiHookAPI 2022-05-31 02:13:53 +08:00
63512254da Update version to 1.8 2022-05-30 03:58:50 +08:00
69c5003af1 Changed contributing way 2022-05-30 03:46:30 +08:00
ac17f3ae55 Added notify icon in notify panel used app icon function 2022-05-30 03:24:40 +08:00
d469adc667 Added warn dialog when enable replace all notify icon 2022-05-30 02:34:39 +08:00
2bb2218145 Merge code 2022-05-30 02:09:45 +08:00
aae8a27fcc Merge to new way support receiver callback 2022-05-30 01:58:28 +08:00
3b31e2c255 Merge systemBar support with native 2022-05-30 00:52:03 +08:00
f111e7d53c Added YukiPromoteTool 2022-05-30 00:38:08 +08:00
327f2139b8 Update YukiHookAPI 2022-05-29 04:08:16 +08:00
adb1f174ad Update YukiHookAPI 2022-05-27 03:36:28 +08:00
373d5c6cb8 Merge code 2022-05-25 04:38:40 +08:00
aba5e3c23c Update YukiHookAPI 2022-05-25 04:27:01 +08:00
8a2f0aca83 Changed FunctionFactory.kt 2022-05-12 01:10:22 +08:00
a0a4d08612 Changed Xposed Scope 2022-05-11 21:52:55 +08:00
25747b2041 Update YukiHookAPI 2022-05-10 01:53:17 +08:00
8de1abf679 Update version to 1.77 2022-05-09 16:08:08 +08:00
b97de07843 Update misc 2022-05-09 16:01:41 +08:00
b497189549 修复媒体通知面板不能被透明的问题,新增媒体通知自动展开功能 2022-05-09 16:01:35 +08:00
1ca659dd6a Merge code 2022-05-09 15:00:08 +08:00
137 changed files with 4664 additions and 3683 deletions

33
.editorconfig Normal file
View File

@@ -0,0 +1,33 @@
# noinspection EditorConfigKeyCorrectness
[{*.kt,*.kts}]
ktlint_standard_annotation = disabled
ktlint_standard_filename = disabled
ktlint_standard_wrapping = disabled
ktlint_standard_import-ordering = enabled
ktlint_standard_max-line-length = disabled
ktlint_standard_multiline-if-else = disabled
ktlint_standard_argument-list-wrapping = disabled
ktlint_standard_parameter-list-wrapping = disabled
ktlint_standard_trailing-comma-on-declaration-site = disabled
ktlint_function_signature_body_expression_wrapping = multiline
ktlint_standard_string-template-indent = disabled
ktlint_standard_function-signature = disabled
ktlint_standard_trailing-comma-on-call-site = disabled
ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_no-empty-first-line-in-class-body = disabled
ktlint_standard_if-else-wrapping = disabled
ktlint_standard_if-else-bracing = disabled
ktlint_standard_statement-wrapping = disabled
ktlint_standard_blank-line-before-declaration = disabled
ktlint_standard_no-empty-file = disabled
ktlint_standard_property-naming = disabled
ktlint_standard_function-naming = disabled
ktlint_standard_chain-method-continuation = disabled
ktlint_standard_class-signature = disabled
ktlint_standard_condition-wrapping = disabled
ktlint_standard_class-signature = disabled
ij_continuation_indent_size = 2
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 150

View File

@@ -1,29 +0,0 @@
---
name: 通知优化图标适配反馈
about: 提交通知图标优化适配必须使用此模板提交
title: "[通知优化图标适配反馈]"
labels: To be adapted
assignees: ''
---
**需要适配的 APP 名称/包名/通知图标颜色 (必填)**
* (示例:阅读/com.heytap.reader/#fff2a135)
*
**提供相关 APP 的下载渠道截图以及简要说明用途 (必填)**
*
**提供相关 APP 的通知单色图标适配素材 大小 50x50 (选填)**
* (可填写资源下载地址或直接添加附件提交,不接受百度网盘、天翼云盘以及各种快传、私有云盘)
* (若直接在附件提交这里可不填)
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
<!--- 请保留模板原始标题 --->
<!--- 不按规定提交的 issues 将直接被关闭 --->
<!--- Create by Template --->

View File

@@ -1,52 +0,0 @@
---
name: 问题与 BUG 反馈
about: 问题反馈必须使用此模板进行提交
title: "[问题与 BUG 反馈] *简要描述问题原因*"
labels: bug
assignees: fankes
---
**系统版本(必填)**
*
**系统类型(请保留一个)**
* ColorOS/RealmeUI/OxygenOS
**Android 版本(必填)**
*
**模块版本(必填)**
*
**使用的 Xposed 框架名称与框架版本(必填)**
* (例如LSPosed Zygisk/Riru 版本号)
**同时使用的带有系统界面作用域的 Xposed 模块(选填)**
* (没有可空)
**问题的具体描述**
* (复现步骤、前提以及详细截图和录屏演示)
**提供模块问题 Log 或必要 Log**
* (LSPosed 可在日志管理中查看并筛选包含 `ColorOSNotifyIcon` 的日志)
<details><summary>展开查看</summary><pre><code>
此处粘贴问题Log
</code></pre></details>
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
<!--- 请保留模板原始标题 --->
<!--- 不按规定提交的 issues 将直接被关闭 --->
<!--- Create by Template --->

93
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,93 @@
name: 问题与 BUG 反馈
description: 问题反馈必须使用此模板进行提交
labels: [bug]
title: "[问题与 BUG 反馈] (在这里简要描述问题原因)"
body:
- type: markdown
attributes:
value: |
### 请在下方填写问题发生的具体原因和复现步骤。
RealmeUI 与 OxygenOS ≥ 12 都是基于 ColorOS 进行修改而来,理论功能是通用的,请额外注意在下方选择正确的系统类型。
我们只接受 ColorOS/RealmeUI/OxygenOS 正规官方版本系统,如果你正在使用官改(第三方修改版)请不要提交任何 BUG 与问题,我们无义务去解决,请自求多福。
发生异常、崩溃、闪退或功能性问题,必须提交问题 Log (日志),没有 Log 的 issues 将直接被关闭。
- type: input
attributes:
label: 模块版本
description: 请填写当前使用的模块完整版本号,例如:**2.0**
validations:
required: true
- type: dropdown
attributes:
label: 系统类型
description: 请选择你使用的系统类型。
options:
- ColorOS
- RealmeUI
- OxygenOS
validations:
required: true
- type: input
attributes:
label: 系统版本
description: 这里填写当前的系统版本,以 ColorOS 举例:**ColorOS 12.1 LE2120_11_C.67**
validations:
required: true
- type: dropdown
attributes:
label: Android 版本
options:
- 15
- 14
- 13
- 12L/12.1
- 12
- 11
- 10
- 9
validations:
required: true
- type: input
attributes:
label: Xposed 框架名称与版本号
description: 请填写当前使用的 Xposed 框架,例如:**LSPosed 1.8.4(次版本号)**
validations:
required: true
- type: input
attributes:
label: 与系统界面(系统 UI)同作用域的 Xposed 模块
description: |
此模块的作用域为系统界面(系统 UI),为确保非其它模块冲突造成的问题,请一定要填写当前你同时激活的相关模块。
若没有,请直接在下方填写“无”。
validations:
required: true
- type: textarea
attributes:
label: 详细描述问题发生的具体原因
description: 请在下方详细描述问题发生的具体场景、复现步骤和经过,以便我们能够按照你所描述的步骤复现这个问题。
validations:
required: true
- type: textarea
attributes:
label: 提供模块问题 Log 或必要 Log
description: LSPosed 可在日志管理中查看并筛选包含 `ColorOSNotifyIcon` 的日志。
value: |
<details><summary>展开查看</summary><pre><code>
(此处粘贴问题 Log)
</code></pre></details>
<!-- 提交时请将括号内容包括括号全部删除,粘贴你复制的日志,不要破坏代码格式 -->
validations:
required: true
- type: checkboxes
attributes:
label: 确认一下你提交的信息
description: |
为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。
请一定确保你已经**勾选下方的选项**后再提交。
options:
- label: 我确保上述信息准确无误
required: false

View File

@@ -0,0 +1,71 @@
name: 通知图标优化适配反馈
description: 提交通知图标优化适配必须使用此模板提交
labels: [To be adapted]
title: "[通知图标优化适配反馈]"
body:
- type: markdown
attributes:
value: |
### 请在下方填写你需要适配的 APP 通知图标的必要信息。
**China and Chinese only.**
**仅限中国和中文。**
以下类型的 APP 不予适配:
- VPN、翻墙软件
- 涉嫌色情、赌博类软件
- 申请超限权限、涉嫌泄露国家机密行为的软件
以下类型的 APP 通知图标暂不适配:
- 多态彩色图标,状态不唯一,例如 360 极速浏览器
- 规范的原生图标,但未被通知图标规则适配的 (将稍后加入白名单)
- type: input
attributes:
label: APP 名称
description: 这里填写 APP 的名称,例如:**微信**
validations:
required: true
- type: input
attributes:
label: APP 包名
description: 这里填写 APP 的包名,例如:**com.tencent.mm**
validations:
required: true
- type: input
attributes:
label: 通知图标颜色 (HEX)
description: |
这里填写通知图标在下拉通知栏中的图标颜色,要求为 16 进制,例如:**#ff232323**
如果不知道什么是 16 进制颜色,可以参考 [这里](https://www.qtccolor.com/tool/hex.aspx) 的取色。
留空代表使用系统默认主题色。
validations:
required: false
- type: input
attributes:
label: 下载渠道、来源地址链接
description: 请填写我们应该从何处得到你需要适配的这个 APP 的下载链接。
validations:
required: true
- type: textarea
attributes:
label: 简单描述适配的通知图标使用场景
description: 简单描述一下当前 APP 的通知图标在何时会变成彩色的、不规范的以及可触发推送通知的操作,例如小米推送或 HMS 推送。
validations:
required: true
- type: textarea
attributes:
label: 通知单色图标适配素材 (大小 50x50~72x72)
description: 请在这里填写我们能够获得这个图标的网址或在下方的文本框粘贴你需要上传的图标。
validations:
required: false
- type: checkboxes
attributes:
label: 确认一下你提交的信息
description: |
为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。
请一定确保你已经**勾选下方的选项**后再提交。
options:
- label: 我确保上述信息准确无误
required: false

88
.github/workflows/commit_ci.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Automatic Build on Commit
on:
workflow_dispatch:
push:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Build CI
if: ${{ success() }}
runs-on: ubuntu-latest
env:
APK_OUTPUT_PATH: 'app/build/outputs/apk'
TG_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TG_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
COMMIT_MESSAGE: |+
New push to GitHub\!
```
${{ github.event.head_commit.message }}
```by `${{ github.event.head_commit.author.name }}`
See commit detail [here](${{ github.event.head_commit.url }})
COMMIT_URL: ${{ github.event.head_commit.url }}
steps:
- uses: actions/checkout@v4
- name: Prepare GitHub Env
run: |
GITHUB_SHA=${{ github.sha }}
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 21
uses: actions/setup-java@v4
with:
java-version: 21
java-package: jdk
distribution: 'temurin'
cache: 'gradle'
- name: Cache Gradle Dependencies
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Build with Gradle
run: |
./gradlew :app:assembleDebug
./gradlew :app:assembleRelease
echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
- name: Upload Artifacts (Debug)
uses: actions/upload-artifact@v4
with:
path: ${{ env.DEBUG_APK_PATH }}
name: ColorOSNotifyIcon-debug-${{ github.event.head_commit.id }}
- name: Upload Artifacts (Release)
uses: actions/upload-artifact@v4
with:
path: ${{ env.RELEASE_APK_PATH }}
name: ColorOSNotifyIcon-release-${{ github.event.head_commit.id }}
- name: Post Artifacts to Telegram
run: |
export debug=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name "*.apk")
export release=$(find ${{ env.APK_OUTPUT_PATH }}/release -name "*.apk")
ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'`
curl -v "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMediaGroup?chat_id=${TG_CHAT_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fdebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Frelease%22%2C%22parse_mode%22%3A%22MarkdownV2%22%2C%22caption%22:${ESCAPED}%7D%5D" \
-F debug="@$debug" \
-F release="@$release"

70
.github/workflows/pull_request_ci.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
name: Pull Request Checker
on:
pull_request:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Pull Request Check
if: ${{ success() }}
runs-on: ubuntu-latest
env:
APK_OUTPUT_PATH: 'app/build/outputs/apk'
steps:
- uses: actions/checkout@v4
- name: Prepare GitHub Env
run: |
GITHUB_SHA=${{ github.sha }}
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 21
uses: actions/setup-java@v4
with:
java-version: 21
java-package: jdk
distribution: 'temurin'
cache: 'gradle'
- name: Cache Gradle Dependencies
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Build with Gradle
run: |
./gradlew :app:assembleDebug
./gradlew :app:assembleRelease
echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
- name: Upload Artifacts (Debug)
uses: actions/upload-artifact@v4
with:
path: ${{ env.DEBUG_APK_PATH }}
name: ColorOSNotifyIcon-debug-${{ github.event.head_commit.id }}
- name: Upload Artifacts (Release)
uses: actions/upload-artifact@v4
with:
path: ${{ env.RELEASE_APK_PATH }}
name: ColorOSNotifyIcon-release-${{ github.event.head_commit.id }}

117
.gitignore vendored
View File

@@ -1,15 +1,110 @@
## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects
## References:
## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
## - https://github.com/android/platform-samples/blob/main/.gitignore
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
.idea/.name
.idea/artifacts
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/modules.xml
.idea/*.iml
.idea/modules
.idea/caches
.idea/material_theme**
.idea/other.xml
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
*.ipr
# Kotlin
.kotlin
# Misc
.idea/misc.xml
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# Android studio 3.1+ additional
.idea/deployment*.xml
.idea/assetWizardSettings.xml
.idea/androidTestResultsUserPreferences.xml
# Android projects
**/local.properties
/captures
.externalNativeBuild
.cxx
local.properties
# Gradle projects
.gradle
build/
# Mkdocs temporary serving folder
docs-gen
site
*.bak
.idea/appInsightsSettings.xml
# Mac OS
.DS_Store

3
.idea/.gitignore generated vendored
View File

@@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

6
.idea/AndroidProjectSystem.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

6
.idea/compiler.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

20
.idea/gradle.xml generated
View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

BIN
.idea/icon.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,10 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

6
.idea/kotlinc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.10" />
</component>
</project>

7
.idea/ktlint-plugin.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KtLint plugin">
<ktlintMode>MANUAL</ktlintMode>
<formatOnSave>false</formatOnSave>
</component>
</project>

6
.idea/ktlint.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KtlintProjectConfiguration">
<treatAsErrors>false</treatAsErrors>
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

28
.idea/misc.xml generated
View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="app/src/main/res/drawable-night/bg_orange_round.xml" value="0.232" />
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.254" />
<entry key="app/src/main/res/drawable/bg_orange_round.xml" value="0.232" />
<entry key="app/src/main/res/drawable/ic_nf_icon_refresh.xml" value="0.232" />
<entry key="app/src/main/res/drawable/ic_nf_icon_update.xml" value="0.232" />
<entry key="app/src/main/res/drawable/ic_notify_icon.xml" value="0.232" />
<entry key="app/src/main/res/drawable/ic_system_clock.xml" value="0.232" />
<entry key="app/src/main/res/layout/activity_config.xml" value="0.4375" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.31170825335892516" />
<entry key="app/src/main/res/layout/adapter_config.xml" value="0.4375" />
<entry key="app/src/main/res/layout/dia_icon_filter.xml" value="0.4375" />
<entry key="app/src/main/res/layout/dia_source_from.xml" value="0.4375" />
<entry key="app/src/main/res/layout/dia_source_from_string.xml" value="0.4375" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

17
.idea/runConfigurations.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

2
.idea/vcs.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>

115
README.md
View File

@@ -1,43 +1,46 @@
# ColorOS 通知图标增强
[![Blank](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/fankes/ColorOSNotifyIcon)
[![Blank](https://img.shields.io/badge/license-AGPL3.0-blue)](https://github.com/fankes/ColorOSNotifyIcon/blob/master/LICENSE)
[![Blank](https://img.shields.io/badge/version-v1.76-green)](https://github.com/fankes/ColorOSNotifyIcon/releases)
[![Blank](https://img.shields.io/github/downloads/fankes/ColorOSNotifyIcon/total?label=Release)](https://github.com/fankes/ColorOSNotifyIcon/releases)
[![Blank](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.coloros.notify/total?label=LSPosed%20Repo&logo=Android&style=flat&labelColor=F48FB1&logoColor=ffffff)](https://github.com/Xposed-Modules-Repo/com.fankes.coloros.notify/releases)
[![Telegram](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](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/>
[![GitHub license](https://img.shields.io/github/license/fankes/ColorOSNotifyIcon?color=blue&style=flat-square)](https://github.com/fankes/ColorOSNotifyIcon/blob/master/LICENSE)
[![GitHub CI](https://img.shields.io/github/actions/workflow/status/fankes/ColorOSNotifyIcon/commit_ci.yml?label=CI%20builds&style=flat-square)](https://github.com/fankes/ColorOSNotifyIcon/actions/workflows/commit_ci.yml)
[![GitHub release](https://img.shields.io/github/v/release/fankes/ColorOSNotifyIcon?display_name=release&logo=github&color=green&style=flat-square)](https://github.com/fankes/ColorOSNotifyIcon/releases)
![GitHub all releases](https://img.shields.io/github/downloads/fankes/ColorOSNotifyIcon/total?label=downloads&style=flat-square)
![GitHub all releases](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.coloros.notify/total?label=LSPosed%20downloads&labelColor=F48FB1&style=flat-square)
[![Telegram CI](https://img.shields.io/badge/CI%20builds-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/ColorOSNotifyIcon_CI)
[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/XiaofangInternet)
[![QQ](https://img.shields.io/badge/discussion-QQ-blue.svg?logo=tencent-qq&logoColor=red&style=flat-square)](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM)
[![QQ 频道](https://img.shields.io/badge/discussion-QQ%20频道-blue.svg?logo=tencent-qq&logoColor=red&style=flat-square)](https://pd.qq.com/s/44gcy28h)
<img src="img-src/icon.png" width = "100" height = "100" alt="LOGO"/>
Optimize notification icons for ColorOS and adapt to native notification icon specifications.
为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeUI。
## Developer
## For Non-Chinese Users
[酷安 @星夜不荟](http://www.coolapk.com/u/876977)
This project will not be adapted i18n, please stay tuned for my new projects in the future.
## 项目迁移公告
由于本人同时维护 **MIUI****ColorOS** 两个系统需要同时维护两个模块,十分不方便,所以我决定在后期逐渐合并两个项目并解耦合为一个新项目并计划适配更多系统与设备,例如原生与类原生系统。
在新的项目确定后,会在这里添加新项目的链接,届时我会终止维护这个项目并建议大家转移到新项目。
## 适配说明
- 此模块仅支持 <b>LSPosed</b>(作用域“系统界面”)、<b>~~EdXposed(随时停止支持)~~</b>、不支持<b>太极、无极</b>
- 此模块仅支持 **LSPosed** (作用域“系统界面”)、**~~EdXposed(随时停止支持)~~**、不支持**太极、无极**
- 目前仅在 ColorOS 12 for OnePlus 上测试通过,如有问题请提交 `issues`
- 目前仅在 ColorOS 12、12.1、13 for OnePlus 上测试通过,如有问题请提交 `issues`
- 建议在不低于 ColorOS 11 的版本上使用
## 请勿用于非法用途
## 注意事项
- 本模块完全开源免费,如果好用你可以打赏支持开发,但是请不要用于非法用途。
由于 ColorOS 15 版本的系统性通知图标行为变更,系统强制在通知图标初始化阶段就将图标强制替换为 APP 彩色图标进行破坏,所以目前加入了 “系统框架”
作用域如果在模块安装后没有自动勾选此作用域请手动进行勾选并重新启动系统以修复此破坏行为ColorOS 15 以下版本的系统无需勾选。
- 本模块发布地址仅有 [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) 及**酷安应用市场**
,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
## 贡献通知图标优化名单
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。
- [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
感谢 [Nep-Timeline](https://github.com/Nep-Timeline) 提供的解决方案。
## 历史背景
@@ -45,21 +48,69 @@ Optimize notification icons for ColorOS and adapt to native notification icon sp
而且从 ColorOS 12 开始,原生图标丢失了着色属性,这也是一种对原生 Android 生态的破坏。
## 捐赠支持
## 贡献通知图标优化名单
- 工作不易,无意外情况此项目将继续维护下去,提供更多可能,欢迎打赏。<br/><br/>
<img src="https://github.com/fankes/YuKiHookAPI/blob/master/img-src/wechat_code.jpg" width = "200" height = "200"/>
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。
- [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
## 发行渠道
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub CI](https://github.com/fankes/ColorOSNotifyIcon/actions/workflows/commit_ci.yml) | CI 自动构建 (测试版) |
|------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|---------------|
| <img src="https://github.com/peter-iakovlev/Telegram/blob/public/Icon.png?raw=true" width = "30" height = "30" alt="LOGO"/> | [Telegram CI 频道](https://t.me/ColorOSNotifyIcon_CI) | CI 自动构建 (测试版) |
|-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|---------------|
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub Releases](https://github.com/fankes/ColorOSNotifyIcon/releases) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|-----------|
| <img src="https://avatars.githubusercontent.com/u/78217009?s=200&v=4?raw=true" width = "30" height = "30" alt="LOGO"/> | [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.coloros.notify/releases) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|-----------|
| <img src="https://github.com/fankes/fankes/assets/37344460/82113d3c-aa7b-4dd1-95c7-cda650065c12" width = "30" height = "30" alt="LOGO"/> | [123 云盘 **(密码al5u)**](https://www.123pan.com/s/5SlUVv-C8DBh.html) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|-----------|
本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
## 注意事项
<h3>1.&nbsp;本软件免费、由兴趣驱动开发,仅供学习交流使用。如果你是从其他非官方渠道付费获得本软件,可能已遭遇欺诈,欢迎向我们举报可疑行为。</h3>
<h3>2.&nbsp;本软件采用 <strong>GNU Affero General Public License (AGPL 3.0)</strong> 许可证。根据该许可证的要求:</h3>
- 任何衍生作品必须采用相同的 AGPL 许可证
- 分发本软件或其修改版本时,必须提供完整的源代码
- 必须保留原始的版权声明及许可证信息
- 不得额外施加限制来限制他人对本软件的自由使用
<h3>3.&nbsp;我们鼓励在遵守 AGPL 3.0 条款的前提下进行自由传播和改进,但请尊重作者署名权,勿冒用原作者名义。</h3>
## 项目推广
<!--suppress HtmlDeprecatedAttribute -->
<div align="center">
<h2>嘿,还请君留步!👋</h2>
<h3>这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。</h3>
<h3>如果下方的项目能为你提供帮助,不妨为我点个 star 吧!</h3>
<h3>所有项目免费、开源,遵循对应开源许可协议。</h3>
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README-zh-CN.md">→ 查看更多关于我的项目,请点击这里 ←</a></h1>
</div>
## Star History
![Star History Chart](https://api.star-history.com/svg?repos=fankes/ColorOSNotifyIcon&type=Date)
## 隐私政策
- [PRIVACY](https://github.com/fankes/ColorOSNotifyIcon/blob/master/PRIVACY.md)
- [PRIVACY](PRIVACY.md)
## 许可证
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
```
Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -72,9 +123,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
```
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com)
版权所有 © 20174 Fankes Studio(qzmmcn@163.com)

3
app/.gitignore vendored
View File

@@ -1 +1,2 @@
/build
/src/main/assets/xposed_init
/src/main/resources/META-INF/yukihookapi_init

View File

@@ -1,86 +0,0 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.devtools.ksp' version '1.6.21-1.0.5'
}
android {
compileSdk 31
signingConfigs {
debug {
storeFile file('../keystore/public')
storePassword '123456'
keyAlias 'public'
keyPassword '123456'
v1SigningEnabled true
v2SigningEnabled true
}
}
defaultConfig {
applicationId "com.fankes.coloros.notify"
minSdk 29
targetSdk 31
versionCode rootProject.ext.appVersionCode
versionName rootProject.ext.appVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled 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'
]
}
buildFeatures {
viewBinding true
}
}
/** 移除无效耗时 lint Task */
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalRelease") task.enabled = false
}
/** 移除无效耗时 lint Task */
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalAnalyzeRelease") task.enabled = false
}
/** 移除无效耗时 lint Task */
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalReportRelease") task.enabled = false
}
dependencies {
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.0.86'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.86'
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.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

89
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,89 @@
plugins {
autowire(libs.plugins.android.application)
autowire(libs.plugins.kotlin.android)
autowire(libs.plugins.kotlin.ksp)
}
android {
namespace = property.project.app.packageName
compileSdk = property.project.android.compileSdk
signingConfigs {
create("universal") {
keyAlias = property.project.app.signing.keyAlias
keyPassword = property.project.app.signing.keyPassword
storeFile = rootProject.file(property.project.app.signing.storeFilePath)
storePassword = property.project.app.signing.storePassword
enableV1Signing = true
enableV2Signing = true
}
}
defaultConfig {
applicationId = property.project.app.packageName
minSdk = property.project.android.minSdk
targetSdk = property.project.android.targetSdk
versionName = property.project.app.versionName
versionCode = property.project.app.versionCode
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
all { signingConfig = signingConfigs.getByName("universal") }
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
buildConfig = true
viewBinding = true
}
lint { checkReleaseBuilds = false }
androidResources.additionalParameters += listOf("--allow-reserved-package-id", "--package-id", "0x37")
}
androidComponents {
onVariants(selector().all()) {
it.outputs.forEach { output ->
val currentType = it.buildType
// Workaround for GitHub Actions.
// Why? I don't know, but it works.
// Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
// public inline fun CharSequence.isNotBlank(): Boolean defined in kotlin.text.
@Suppress("UNNECESSARY_SAFE_CALL", "RemoveRedundantCallsOfConversionMethods")
val currentSuffix = property.github.ci.commit.id?.let { suffix ->
// Workaround for GitHub Actions.
// Strongly transfer type to [String].
val sSuffix = suffix.toString()
if (sSuffix.isNotBlank()) "-$sSuffix" else ""
}
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
if (output is com.android.build.api.variant.impl.VariantOutputImpl)
output.outputFileName.set("${property.project.name}-v$currentVersion-$currentType.apk")
}
}
}
dependencies {
compileOnly(de.robv.android.xposed.api)
implementation(com.highcapable.yukihookapi.api)
ksp(com.highcapable.yukihookapi.ksp.xposed)
implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(com.fankes.projectpromote.project.promote)
implementation(com.github.topjohnwu.libsu.core)
implementation(com.github.duanhong169.drawabletoolbox)
implementation(com.squareup.okhttp3.okhttp)
implementation(androidx.core.core.ktx)
implementation(androidx.appcompat.appcompat)
implementation(com.google.android.material.material)
implementation(androidx.constraintlayout.constraintlayout)
testImplementation(junit.junit)
androidTestImplementation(androidx.test.ext.junit)
androidTestImplementation(androidx.test.espresso.espresso.core)
}

View File

@@ -32,13 +32,15 @@
-adaptresourcefilecontents
-renamesourcefileattribute P
-keepattributes SourceFile,LineNumberTable
-keepattributes SourceFile,Signature,LineNumberTable
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
public static *** throwUninitializedProperty(...);
public static *** throwUninitializedPropertyAccessException(...);
}
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
-keep class * extends android.app.Activity
-keep class * implements androidx.viewbinding.ViewBinding {
<init>();
*** inflate(android.view.LayoutInflater);
}

View File

@@ -1,10 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.fankes.coloros.notify">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
tools:node="remove" />
<permission
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
tools:node="remove" />
<application
android:name=".application.CNNApplication"

View File

@@ -1 +0,0 @@
com.fankes.coloros.notify.hook.HookEntry_YukiHookXposedInit

View File

@@ -1 +0,0 @@
com.fankes.coloros.notify.hook.HookEntry

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,13 +18,14 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/24.
* This file is created by fankes on 2022/1/24.
*/
@file:Suppress("unused")
package com.fankes.coloros.notify.application
import androidx.appcompat.app.AppCompatDelegate
import com.fankes.coloros.notify.data.ConfigData
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
class CNNApplication : ModuleApplication() {
@@ -33,5 +34,7 @@ class CNNApplication : ModuleApplication() {
super.onCreate()
/** 跟随系统夜间模式 */
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
/** 装载存储控制类 */
ConfigData.init(instance = this)
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,7 +18,7 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/30.
* This file is created by fankes on 2022/1/30.
*/
package com.fankes.coloros.notify.bean
@@ -47,13 +47,15 @@ data class IconDataBean(
) : Serializable {
fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all"
override fun toString() = "{\n" +
" \"appName\": \"$appName\",\n" +
" \"packageName\": \"$packageName\",\n" +
" \"iconBitmap\": \"${iconBitmap.base64}\",\n" +
" \"iconColor\": \"#${Integer.toHexString(iconColor)}\",\n" +
" \"contributorName\": \"$contributorName\",\n" +
" \"isEnabled\": $isEnabled,\n" +
" \"isEnabledAll\": $isEnabledAll\n" +
" }"
override fun toString() = """
{
"appName": "$appName",
"packageName": "$packageName",
"iconBitmap": "${iconBitmap.base64}",
"iconColor": "#${Integer.toHexString(iconColor)}",
"contributorName": "$contributorName",
"isEnabled": $isEnabled,
"isEnabledAll": $isEnabledAll
}
""".trimIndent()
}

View File

@@ -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}_202205090117"
/** 当前模块的版本校验标签 */
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"
}

View File

@@ -0,0 +1,81 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/2/2.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.coloros.notify.const
import com.fankes.coloros.notify.generated.AppProperties
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
/**
* 包名常量定义类
*/
object PackageName {
/** 系统框架 */
const val SYSTEM_FRAMEWORK = "android"
/** 系统界面 (系统 UI) */
const val SYSTEMUI = "com.android.systemui"
}
/**
* 通知图标优化名单同步方式定义类
*/
object IconRuleSourceSyncType {
/** GitHub Raw (代理 - GitHub Proxy) */
const val GITHUB_RAW_PROXY_1 = 500
/** GitHub Raw (代理 - 7ED Services) */
const val GITHUB_RAW_PROXY_2 = 1000
/** GitHub Raw (直连) */
const val GITHUB_RAW_DIRECT = 2000
/** 自定义地址 */
const val CUSTOM_URL = 3000
}
/**
* 模块版本常量定义类
*/
object ModuleVersion {
/** 当前 GitHub 提交的 ID (CI 自动构建) */
const val GITHUB_COMMIT_ID = AppProperties.GITHUB_CI_COMMIT_ID
/** 版本名称 */
const val NAME = BuildConfigWrapper.VERSION_NAME
/** 版本号 */
const val CODE = BuildConfigWrapper.VERSION_CODE
/** 是否为 CI 自动构建版本 */
val isCiMode = GITHUB_COMMIT_ID.isNotBlank()
/** 当前版本名称后缀 */
val suffix = GITHUB_COMMIT_ID.let { if (it.isNotBlank()) "-$it" else "" }
override fun toString() = "$NAME$suffix($CODE)"
}

View File

@@ -0,0 +1,389 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/2/2.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.coloros.notify.data
import android.content.Context
import com.fankes.coloros.notify.const.IconRuleSourceSyncType
import com.fankes.coloros.notify.utils.factory.isUpperOfAndroidS
import com.highcapable.yukihookapi.hook.factory.prefs
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
/**
* 全局配置存储控制类
*/
object ConfigData {
/** 启用模块 */
val ENABLE_MODULE = PrefsData("_enable_module", true)
/** 启用模块日志 */
val ENABLE_MODULE_LOG = PrefsData("_enable_module_log", false)
/** 启用通知图标兼容模式 */
val ENABLE_COLOR_ICON_COMPAT = PrefsData("_color_icon_compat", false)
/** 移除开发者选项警告通知 */
val ENABLE_REMOVE_DEV_NOTIFY = PrefsData("_remove_dev_notify", true)
/** 移除充电完成通知 */
val ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY = PrefsData("_remove_charge_complete_notify", false)
/** 移除免打扰通知 */
val ENABLE_REMOVE_DND_ALERT_NOTIFY = PrefsData("_remove_dndalert_notify", false)
/** 启用 Material 3 通知图标风格 */
val ENABLE_MD3_NOTIFY_ICON_STYLE = PrefsData("_notify_icon_md3_style", isUpperOfAndroidS)
/** 通知栏中的通知图标圆角程度 */
val NOTIFY_ICON_CORNER_SIZE = PrefsData("_notify_icon_corner", 15)
/** 强制通知栏中的通知图标使用系统着色 */
val ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR = PrefsData("_notify_icon_force_system_color", false)
/** 强制通知栏中的通知图标为 APP 图标 */
val ENABLE_NOTIFY_ICON_FORCE_APP_ICON = PrefsData("_notify_icon_force_app_icon", false)
/** 启用媒体通知播放时自动展开 */
val ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP = PrefsData("_enable_notify_media_panel_auto_exp", false)
/** 启用自定义通知面板背景透明度 */
val ENABLE_NOTIFY_PANEL_ALPHA = PrefsData("_enable_notify_panel_alpha_pst", false)
/** 自定义通知面板背景透明度 */
val NOTIFY_PANEL_ALPHA_LEVEL = PrefsData("_notify_panel_alpha_pst", 75)
/** 启用通知图标优化 */
val ENABLE_NOTIFY_ICON_FIX = PrefsData("_notify_icon_fix", true)
/** 使用占位符修补未适配的通知图标 */
val ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER = PrefsData("_notify_icon_fix_placeholder", false)
/** 提醒未适配通知图标的新安装应用 */
val ENABLE_NOTIFY_ICON_FIX_NOTIFY = PrefsData("_notify_icon_fix_notify", true)
/** 启用通知图标优化名单自动更新 */
val ENABLE_NOTIFY_ICON_FIX_AUTO = PrefsData("_enable_notify_icon_fix_auto", true)
/** 通知图标优化名单自动更新时间 */
val NOTIFY_ICON_FIX_AUTO_TIME = PrefsData("_notify_icon_fix_auto_time", "07:00")
/** 通知图标优化适配数据 */
val NOTIFY_ICONS_DATA = PrefsData("_notify_icon_datas", "")
/** 通知图标优化名单同步方式 */
val ICON_RULE_SOURCE_SYNC_TYPE = PrefsData("_rule_source_sync_way", IconRuleSourceSyncType.GITHUB_RAW_PROXY_1)
/** 通知图标优化名单同步地址 */
val ICON_RULE_SOURCE_SYNC_CUSTOM_URL = PrefsData("_rule_source_sync_way_custom_url", "")
/** 当前实例 - [Context] or [PackageParam] */
private var instance: Any? = null
/**
* 初始化存储控制类
* @param instance 实例 - 只能是 [Context] or [PackageParam]
* @throws IllegalStateException 如果类型错误
*/
fun init(instance: Any) {
when (instance) {
is Context, is PackageParam -> this.instance = instance
else -> error("Unknown type for init ConfigData")
}
}
/**
* 读取 [String] 数据
* @param data 键值数据模板
* @return [String]
*/
private fun getString(data: PrefsData<String>) = when (instance) {
is Context -> (instance as Context).prefs().get(data)
is PackageParam -> (instance as PackageParam).prefs.get(data)
else -> error("Unknown type for get prefs data")
}
/**
* 存入 [String] 数据
* @param data 键值数据模板
* @param value 键值内容
*/
private fun putString(data: PrefsData<String>, value: String) {
when (instance) {
is Context -> (instance as Context).prefs().edit { put(data, value) }
is PackageParam -> YLog.warn("Not support for this method")
else -> error("Unknown type for put prefs data")
}
}
/**
* 读取 [Int] 数据
* @param data 键值数据模板
* @return [Int]
*/
internal fun getInt(data: PrefsData<Int>) = when (instance) {
is Context -> (instance as Context).prefs().get(data)
is PackageParam -> (instance as PackageParam).prefs.get(data)
else -> error("Unknown type for get prefs data")
}
/**
* 存入 [Int] 数据
* @param data 键值数据模板
* @param value 键值内容
*/
internal fun putInt(data: PrefsData<Int>, value: Int) {
when (instance) {
is Context -> (instance as Context).prefs().edit { put(data, value) }
is PackageParam -> YLog.warn("Not support for this method")
else -> error("Unknown type for put prefs data")
}
}
/**
* 读取 [Boolean] 数据
* @param data 键值数据模板
* @return [Boolean]
*/
internal fun getBoolean(data: PrefsData<Boolean>) = when (instance) {
is Context -> (instance as Context).prefs().get(data)
is PackageParam -> (instance as PackageParam).prefs.get(data)
else -> error("Unknown type for get prefs data")
}
/**
* 存入 [Boolean] 数据
* @param data 键值数据模板
* @param value 键值内容
*/
internal fun putBoolean(data: PrefsData<Boolean>, value: Boolean) {
when (instance) {
is Context -> (instance as Context).prefs().edit { put(data, value) }
is PackageParam -> YLog.warn("Not support for this method")
else -> error("Unknown type for put prefs data")
}
}
/**
* 是否启用模块
* @return [Boolean]
*/
var isEnableModule
get() = getBoolean(ENABLE_MODULE)
set(value) {
putBoolean(ENABLE_MODULE, value)
}
/**
* 是否启用模块日志
* @return [Boolean]
*/
var isEnableModuleLog
get() = getBoolean(ENABLE_MODULE_LOG)
set(value) {
putBoolean(ENABLE_MODULE_LOG, value)
}
/**
* 是否启用通知图标兼容模式
* @return [Boolean]
*/
var isEnableColorIconCompat
get() = getBoolean(ENABLE_COLOR_ICON_COMPAT)
set(value) {
putBoolean(ENABLE_COLOR_ICON_COMPAT, value)
}
/**
* 是否移除开发者选项警告通知
* @return [Boolean]
*/
var isEnableRemoveDevNotify
get() = getBoolean(ENABLE_REMOVE_DEV_NOTIFY)
set(value) {
putBoolean(ENABLE_REMOVE_DEV_NOTIFY, value)
}
/**
* 是否移除充电完成通知
* @return [Boolean]
*/
var isEnableRemoveChangeCompleteNotify
get() = getBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY)
set(value) {
putBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY, value)
}
/**
* 是否移除免打扰通知
* @return [Boolean]
*/
var isEnableRemoveDndAlertNotify
get() = getBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY)
set(value) {
putBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY, value)
}
/**
* 是否启用 material 3 通知图标风格
* @return [Boolean]
*/
var isEnableMd3NotifyIconStyle
get() = getBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE)
set(value) {
putBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE, value)
}
/**
* 通知栏中的通知图标圆角程度
* @return [Int]
*/
var notifyIconCornerSize
get() = getInt(NOTIFY_ICON_CORNER_SIZE)
set(value) {
putInt(NOTIFY_ICON_CORNER_SIZE, value)
}
/**
* 是否强制通知栏中的通知图标使用系统着色
* @return [Boolean]
*/
var isEnableNotifyIconForceSystemColor
get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR)
set(value) {
putBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR, value)
}
/**
* 是否强制通知栏中的通知图标为 APP 图标
* @return [Boolean]
*/
var isEnableNotifyIconForceAppIcon
get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON)
set(value) {
putBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON, value)
}
/**
* 是否启用媒体通知播放时自动展开
* @return [Boolean]
*/
var isEnableNotifyMediaPanelAutoExp
get() = getBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP)
set(value) {
putBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP, value)
}
/**
* 是否启用自定义通知面板背景透明度
* @return [Boolean]
*/
var isEnableNotifyPanelAlpha
get() = getBoolean(ENABLE_NOTIFY_PANEL_ALPHA)
set(value) {
putBoolean(ENABLE_NOTIFY_PANEL_ALPHA, value)
}
/**
* 自定义通知面板背景透明度
* @return [Int]
*/
var notifyPanelAlphaLevel
get() = getInt(NOTIFY_PANEL_ALPHA_LEVEL)
set(value) {
putInt(NOTIFY_PANEL_ALPHA_LEVEL, value)
}
/**
* 是否启用通知图标优化
* @return [Boolean]
*/
var isEnableNotifyIconFix
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX)
set(value) {
putBoolean(ENABLE_NOTIFY_ICON_FIX, value)
}
/**
* 是否使用占位符修补未适配的通知图标
* @return [Boolean]
*/
var isEnableNotifyIconFixPlaceholder
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER)
set(value) {
putBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER, value)
}
/**
* 是否提醒未适配通知图标的新安装应用
* @return [Boolean]
*/
var isEnableNotifyIconFixNotify
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY)
set(value) {
putBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, value)
}
/**
* 是否启用通知图标优化名单自动更新
* @return [Boolean]
*/
var isEnableNotifyIconFixAuto
get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO)
set(value) {
putBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO, value)
}
/**
* 通知图标优化名单自动更新时间
* @return [String]
*/
var notifyIconFixAutoTime
get() = getString(NOTIFY_ICON_FIX_AUTO_TIME)
set(value) {
putString(NOTIFY_ICON_FIX_AUTO_TIME, value)
}
/**
* 通知图标优化名单同步方式
* @return [Int]
*/
var iconRuleSourceSyncType
get() = getInt(ICON_RULE_SOURCE_SYNC_TYPE)
set(value) {
putInt(ICON_RULE_SOURCE_SYNC_TYPE, value)
}
/**
* 通知图标优化名单同步地址
* @return [String]
*/
var iconRuleSourceSyncCustomUrl
get() = getString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL)
set(value) {
putString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL, value)
}
}

View File

@@ -1,48 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/3/28.
*/
package com.fankes.coloros.notify.data
import com.fankes.coloros.notify.hook.HookConst
import com.fankes.coloros.notify.utils.factory.isUpperOfAndroidS
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
object DataConst {
val ENABLE_MODULE = PrefsData("_enable_module", true)
val ENABLE_MODULE_LOG = PrefsData("_enable_module_log", false)
val ENABLE_HIDE_ICON = PrefsData("_hide_icon", false)
val ENABLE_ANDROID12_STYLE = PrefsData("_notify_android12_style", isUpperOfAndroidS)
val ENABLE_NOTIFY_ICON_FIX = PrefsData("_notify_icon_fix", true)
val ENABLE_NOTIFY_ICON_FIX_NOTIFY = PrefsData("_notify_icon_fix_notify", true)
val REMOVE_DEV_NOTIFY = PrefsData("_remove_dev_notify", true)
val REMOVE_CHANGECP_NOTIFY = PrefsData("_remove_charge_complete_notify", false)
val REMOVE_DNDALERT_NOTIFY = PrefsData("_remove_dndalert_notify", false)
val ENABLE_NOTIFY_ICON_FIX_AUTO = PrefsData("_enable_notify_icon_fix_auto", true)
val ENABLE_NOTIFY_PANEL_ALPHA = PrefsData("_enable_notify_panel_alpha", false)
val NOTIFY_PANEL_ALPHA = PrefsData("_notify_panel_alpha", 185)
val NOTIFY_ICON_DATAS = PrefsData("_notify_icon_datas", "")
val NOTIFY_ICON_FIX_AUTO_TIME = PrefsData("_notify_icon_fix_auto_time", "07:00")
val SOURCE_SYNC_WAY = PrefsData("_rule_source_sync_way", HookConst.TYPE_SOURCE_SYNC_WAY_1)
val SOURCE_SYNC_WAY_CUSTOM_URL = PrefsData("_rule_source_sync_way_custom_url", "")
}

View File

@@ -0,0 +1,102 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/2/3.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.fankes.coloros.notify.data.factory
import android.widget.CompoundButton
import com.fankes.coloros.notify.data.ConfigData
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
/**
* 绑定到 [CompoundButton] 自动设置选中状态
* @param data 键值数据模板
* @param initiate 方法体
*/
fun CompoundButton.bind(data: PrefsData<Boolean>, initiate: CompoundButtonDataBinder.(CompoundButton) -> Unit = {}) {
val binder = CompoundButtonDataBinder(button = this).also { initiate(it, this) }
isChecked = ConfigData.getBoolean(data).also { binder.initializeCallback?.invoke(it) }
binder.applyChangesCallback = { ConfigData.putBoolean(data, it) }
setOnCheckedChangeListener { button, isChecked ->
if (button.isPressed) {
if (binder.isAutoApplyChanges) binder.applyChangesCallback?.invoke(isChecked)
binder.changedCallback?.invoke(isChecked)
}
}
}
/**
* [CompoundButton] 数据绑定管理器实例
* @param button 当前实例
*/
class CompoundButtonDataBinder(private val button: CompoundButton) {
/** 状态初始化回调事件 */
internal var initializeCallback: ((Boolean) -> Unit)? = null
/** 状态改变回调事件 */
internal var changedCallback: ((Boolean) -> Unit)? = null
/** 应用更改回调事件 */
internal var applyChangesCallback: ((Boolean) -> Unit)? = null
/** 是否启用自动应用更改 */
var isAutoApplyChanges = true
/**
* 监听状态初始化
* @param result 回调结果
*/
fun onInitialize(result: (Boolean) -> Unit) {
initializeCallback = result
}
/**
* 监听状态改变
* @param result 回调结果
*/
fun onChanged(result: (Boolean) -> Unit) {
changedCallback = result
}
/** 重新初始化 */
fun reinitialize() {
initializeCallback?.invoke(button.isChecked)
}
/** 应用更改并重新初始化 */
fun applyChangesAndReinitialize() {
applyChanges()
reinitialize()
}
/** 应用更改 */
fun applyChanges() {
applyChangesCallback?.invoke(button.isChecked)
}
/** 取消更改 */
fun cancelChanges() {
button.isChecked = button.isChecked.not()
}
}

View File

@@ -0,0 +1,57 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/2/4.
*/
@file:Suppress("SetTextI18n")
package com.fankes.coloros.notify.data.factory
import android.widget.SeekBar
import android.widget.TextView
import com.fankes.coloros.notify.data.ConfigData
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
/**
* 绑定到 [SeekBar] 自动设置进度与 [TextView]
* @param data 键值数据模板
* @param textView 文本框
* @param suffix 文本显示的后缀 - 默认空
* @param onChange 当改变停止时回调
*/
fun SeekBar.bind(data: PrefsData<Int>, textView: TextView, suffix: String = "", onChange: (Int) -> Unit = {}) {
ConfigData.getInt(data).also { value ->
textView.text = "$value$suffix"
progress = value
}
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
textView.text = "$progress$suffix"
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
ConfigData.putInt(data, seekBar.progress)
onChange(seekBar.progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
})
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,36 +18,41 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/2/26.
* This file is created by fankes on 2022/2/26.
*/
package com.fankes.coloros.notify.hook
import com.fankes.coloros.notify.data.DataConst
import com.fankes.coloros.notify.hook.HookConst.SYSTEMUI_PACKAGE_NAME
import com.fankes.coloros.notify.const.PackageName
import com.fankes.coloros.notify.data.ConfigData
import com.fankes.coloros.notify.hook.entity.FrameworkHooker
import com.fankes.coloros.notify.hook.entity.SystemUIHooker
import com.fankes.coloros.notify.utils.factory.isNotColorOS
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.factory.configs
import com.highcapable.yukihookapi.hook.factory.encase
import com.highcapable.yukihookapi.hook.log.loggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
@InjectYukiHookWithXposed
class HookEntry : IYukiHookXposedInit {
object HookEntry : IYukiHookXposedInit {
override fun onInit() = configs {
debugTag = "ColorOSNotify"
debugLog {
tag = "ColorOSNotifyIcon"
isRecord = true
elements(PRIORITY)
}
isDebug = false
}
override fun onHook() = encase {
when {
/** 不是 ColorOS 系统停止 Hook */
isNotColorOS -> loggerW(msg = "Aborted Hook -> This System is not ColorOS")
/** Hook 被手动关闭停止 Hook */
prefs.get(DataConst.ENABLE_MODULE).not() -> loggerW(msg = "Aborted Hook -> Hook Closed")
/** 开始 Hook */
else -> loadApp(SYSTEMUI_PACKAGE_NAME, SystemUIHooker)
if (isNotColorOS) return@encase YLog.warn("Aborted Hook -> This System is not ColorOS")
loadSystem(FrameworkHooker)
loadApp(PackageName.SYSTEMUI) {
ConfigData.init(instance = this)
if (ConfigData.isEnableModule)
loadHooker(SystemUIHooker)
else YLog.warn("Aborted Hook -> Hook Closed")
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by Nep-Timeline on 2025/5/27.
*/
package com.fankes.coloros.notify.hook.entity
import android.app.Notification
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
/**
* 系统框架核心 Hook 类
*/
object FrameworkHooker : YukiBaseHooker() {
/** ColorOS 存在的类 - 旧版本不存在 */
private val OplusNotificationFixHelperClass by lazyClassOrNull("com.android.server.notification.OplusNotificationFixHelper")
override fun onHook() {
/** 拦截 ColorOS 覆盖应用通知图标 */
OplusNotificationFixHelperClass?.resolve()?.optional()?.firstMethodOrNull {
name = "fixSmallIcon"
parameters(Notification::class, String::class, String::class, Boolean::class)
}?.hook()?.intercept()
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,19 +18,22 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/24.
* This file is created by fankes on 2022/1/24.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.coloros.notify.param
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import com.fankes.coloros.notify.bean.IconDataBean
import com.fankes.coloros.notify.data.DataConst
import com.fankes.coloros.notify.utils.factory.*
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.fankes.coloros.notify.data.ConfigData
import com.fankes.coloros.notify.utils.factory.bitmap
import com.fankes.coloros.notify.utils.factory.safeOf
import com.fankes.coloros.notify.utils.factory.safeOfNan
import com.fankes.coloros.notify.utils.factory.safeOfNull
import com.fankes.coloros.notify.utils.factory.snake
import com.highcapable.yukihookapi.hook.factory.prefs
import com.highcapable.yukihookapi.hook.param.PackageParam
import org.json.JSONArray
import org.json.JSONObject
@@ -44,64 +47,11 @@ import org.json.JSONObject
*/
class IconPackParams(private val context: Context? = null, private val param: PackageParam? = null) {
companion object {
/**
* Android 11 系统默认图标
* @return [Bitmap]
*/
val android11IconBitmap by lazy {
("iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
"CHwIZIgAAAPkSURBVHic7ZvfVdswFMY/9fS93qDZoNmg6QS4E8AGdAMYgQ1gg4QJkk5AOkGcCUgm\n" +
"+Pog27EdCa7+WDIn/J4gx77W/SzpyvdKwCefdFE5H06yAPCj/vefUuqQqy1JhCA5B3AFYFH/NAdQ\n" +
"WC4/ANjWf28APCultpZrozGaECQbx0sAs0BzFYAVgI1S6jm0baNDsiB5R/KV4/FK8i63r1ZIXpPc\n" +
"jSjAkB3J69x+t5AsEwtgEqTMLcJjRgGGPOYQoCC5zu25gTV1SE4iwpx5h8J77KjDtRNO4ZPkAsAS\n" +
"9jXAVDgA+K2U2khvEAtRq7zG9EVoOAD4JV2MfZFcRD3uXHvCEcBfh+vf429tU0oBYMmYcwbJF+H4\n" +
"3FKH09ng/jnJG5IHh7F+qO+ZD2zN6mdshXbWsUSQhsh7ga2C5Epga0XBmyR5L2xbWGilVl6C04KG\n" +
"5MMbth6m0MbhQyRhcuVhtyBZGWxV9BjTlPWynavdxviNwLhXw2v7C4M95/hf27IJO+TGx7CkNzh1\n" +
"Y8Nzuo0PyjmQfBK019orbOHzD2Q5hMqr1Se6zse0ZWNGy6RuE+I24sOl98e09RZG386EoJ5dU60e\n" +
"xUvgiBQ0RBBTj1gYfkuB10Tpef+ZjyYhrvzbEsT3wPtdhDjzsSdEHb5CE62+zEPCJ4CfDrfMhs8a\n" +
"9oi8KS//j6RHuL/A3vAYCpFrfmiYAXj3m6VLPfH5vMDePaLP8MTckhT1DOq0/jJ6C+hejwjqQTQv\n" +
"sxteqUsEps/wK8pTA1b7XbtfB22bUvapAPAEACQBveZ4q1ToY7+lHRpMlf31Z4HIL6rrc3eOCF3Q\n" +
"fERan6c4WWahK8TopfcJ0vrcCpFzk0Yuuj5f8tDolQaGQsSsQ0yd3lRwyT2ix1CIHImSXPSy70Mh\n" +
"nFPzH5jeS+8JURdM90mbk4f9sDhsmiMuoVec+WgS4hLmiTMfz4RQSq3gVn4PIUci6Fj72MMWPqUV\n" +
"rJSZ51jIq3OU1xJDS36p92K5V9MoKwLvGLcIPDZuReBOYyW9wjlnSHmROSb+tVXm2SgyFmGlCsrK\n" +
"7aRgozh1T1iO56uVpyAROg5shA98oc4wmzaTXXPcXfs2ROsi0T5L6glxC7f6ZHMAJWfRaA9gLkk6\n" +
"uW443QD4FtCwlBwBLKJuOAXaD7IS6VadIRwBlKMegarHuySs5qKiZ1XdR4yC8gk0JRvmKFRRHlpT\n" +
"ECdEBohRMu9QqZj7KFMX6m+TlIJU9P12SAH1RnGXXfiuHCjYAO/KmAdgS5wOwIZuFNvjdAB2lFRi\n" +
"yiPRjTCATsjYFmZHnIovjfMf90i0BOpQ18T87SXWXz+ZKv8BVnFXPlKejoIAAAAASUVORK5CYII=").bitmap
}
/**
* Android 12 系统默认图标
* @return [Bitmap]
*/
val android12IconBitmap by lazy {
("iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAAAXNSR0IArs4c6QAAAARzQklUCAgI\n" +
"CHwIZIgAAANkSURBVHic7ZvNkRoxEIWfXL4vIbBVvpsMTAhksIRgRwAZsBnAxWfjCMARGM4+MBkw\n" +
"RPB8kKh1zagljUYzwlv6qrZ2a0fo50lq9agboFAoFApeVI5GSX4xf07NDwDUAE4AbkqpU45+9Ybk\n" +
"hOTK8/yF5JbklX6uJH+QfPG0uyI5ST+iCMwgf5sBbC3PVoGDd4nSGrARlabtvGI0RLizNc9eegrQ\n" +
"5EJy0RDhTj4xBBHu/EkoQGjdvcT40EOLKYBn4dmnHvX6kOp+xpvh7UyvU4PkDMARwFOHj50B7KBP\n" +
"iEopVZmZnOHtFFkA+NyhzhuAedbThuSMZB2wpHckg2eM5JzkMaDem5mQ/JD86ehoTXLeo+6lR+jv\n" +
"KccSjZk5iRMTWHP6V1200MmgPtYGE+GfdlxiXFK1E9u5pdCxmh3sQYf2Jg4xlqnb69IxaTUsBmxz\n" +
"LbR5GKpNX4dmQoeOI7RdCW1Hnx59HCrJQL02/0FySnJD8kDtHovbJrDsumOfhoN297oWyjZnUDRu\n" +
"oWVptxX7VOMLRliaO0s56XhtLeOOZfe2grHj6bM1bFSJ63OR//KGsqG0nhZsL3dRsNCyJBehqyeE\n" +
"jzEfAiA5SlYbAW3EvkK/WFWQjV2XshWAX96eDoljL+d3dSNJbSP+W4oQhiKEoQhhiD01JDYUvMsR\n" +
"+RZzZZdaiEe4Mou6Aylbw1CEMBQhDLE2okZu91bbgi6xj/dJaje/bA1DEcJQhDAUIQxRp4a5BdpY\n" +
"HkW5t49AHxfbZp2nGO8uUQoJRN2bRm0Nx6yP+a5hFUIpNZ4QDrILMTq0xxWuI7ZvS1SL3pZ9VoQt\n" +
"xjnhCFFp6rCB7XW7FWAaHMqxjcFzFajjosliGik6dBI65Ipb9G1TCuyMGWVrdUpKFLlygARQ6ki5\n" +
"lMSaL1HEdE7KVbikXKp0J7jmd+LoTia7phDDIwL5KBE2kq8eMZxZ9p6653TndI9/UkiYGZMM551D\n" +
"l5mjtgfN5PMmydKUkn1xhdo4HuG/PjsB2EO/E1QAzkqpmnoLPUF7p0v4vdQzdNpx7jhKm8CVkYKk\n" +
"OZyDYMTYDSjC49iEEKgdH+lojaFmbl8hFurVsWZY9r5LgDUffSuEQu2F7gNFqam313IsAXJ93XEG\n" +
"/fZ4/w3o06QGAKXU4Nm7hUKhUIjgL/9/6dhvvYPfAAAAAElFTkSuQmCC").bitmap
}
}
/**
* 已存储的 JSON 数据
* @return [String]
*/
internal val storageDataJson get() = (context?.modulePrefs ?: param?.prefs)?.direct()?.get(DataConst.NOTIFY_ICON_DATAS)
internal val storageDataJson get() = (context?.prefs() ?: param?.prefs)?.get(ConfigData.NOTIFY_ICONS_DATA)
/**
* 获取图标数据
@@ -146,7 +96,7 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
* @return [String] 拼接后的数据
*/
fun splicingJsonArray(dataJson1: String, dataJson2: String) = safeOf(default = "[]") {
dataJson1.replace(oldValue = "]", newValue = "") + "," + dataJson2.replace(oldValue = "[", newValue = "")
dataJson1.replace("]", "") + "," + dataJson2.replace("[", "")
}
/**
@@ -175,7 +125,7 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
* @param json 数据
* @return [Boolean]
*/
fun isHackString(json: String) = json.contains(other = "Checking your browser before accessing")
fun isHackString(json: String) = json.contains("Checking your browser before accessing")
/**
* 比较图标数据不相等
@@ -188,5 +138,5 @@ class IconPackParams(private val context: Context? = null, private val param: Pa
* 保存图标数据
* @param dataJson 图标数据 JSON
*/
fun save(dataJson: String) = context?.modulePrefs?.put(DataConst.NOTIFY_ICON_DATAS, dataJson)
}
fun save(dataJson: String) = context?.prefs()?.edit { put(ConfigData.NOTIFY_ICONS_DATA, dataJson) }
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,13 +18,14 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/2/15.
* This file is created by fankes on 2022/2/15.
* This file is Modified by fankes on 2023/2/3.
*/
package com.fankes.coloros.notify.hook.factory
package com.fankes.coloros.notify.param.factory
import android.content.Context
import com.fankes.coloros.notify.bean.IconDataBean
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.factory.prefs
import com.highcapable.yukihookapi.hook.param.PackageParam
/**
@@ -37,14 +38,14 @@ fun PackageParam.isAppNotifyHookOf(bean: IconDataBean) = prefs.getBoolean(bean.t
* 获取此 APP 的通知图标是否被 Hook
* @param bean 图标 bean
*/
fun Context.isAppNotifyHookOf(bean: IconDataBean) = modulePrefs.getBoolean(bean.toEnabledName(), bean.isEnabled)
fun Context.isAppNotifyHookOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledName(), bean.isEnabled)
/**
* 设置 Hook APP 的通知图标
* @param bean 图标 bean
* @param isHook 是否 Hook
*/
fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = modulePrefs.putBoolean(bean.toEnabledName(), isHook)
fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledName(), isHook) }
/**
* 获取此 APP 的通知图标是否被全部 Hook
@@ -56,11 +57,11 @@ fun PackageParam.isAppNotifyHookAllOf(bean: IconDataBean) = prefs.getBoolean(bea
* 获取此 APP 的通知图标是否被全部 Hook
* @param bean 图标 bean
*/
fun Context.isAppNotifyHookAllOf(bean: IconDataBean) = modulePrefs.getBoolean(bean.toEnabledAllName(), bean.isEnabledAll)
fun Context.isAppNotifyHookAllOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledAllName(), bean.isEnabledAll)
/**
* 设置全部 Hook APP 的通知图标
* @param bean 图标 bean
* @param isHook 是否 Hook
*/
fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = modulePrefs.putBoolean(bean.toEnabledAllName(), isHook)
fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledAllName(), isHook) }

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,7 +18,7 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/3/26.
* This file is created by fankes on 2022/3/26.
*/
package com.fankes.coloros.notify.service

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,32 +18,38 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/30.
* This file is created by fankes on 2022/1/30.
*/
@file:Suppress("SetTextI18n", "InflateParams", "DEPRECATION")
@file:Suppress("SetTextI18n", "InflateParams")
package com.fankes.coloros.notify.ui.activity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import androidx.core.view.isVisible
import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.bean.IconDataBean
import com.fankes.coloros.notify.data.ConfigData
import com.fankes.coloros.notify.databinding.ActivityConfigBinding
import com.fankes.coloros.notify.databinding.AdapterConfigBinding
import com.fankes.coloros.notify.databinding.DiaIconFilterBinding
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf
import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookAllOf
import com.fankes.coloros.notify.hook.factory.putAppNotifyHookOf
import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.param.factory.isAppNotifyHookAllOf
import com.fankes.coloros.notify.param.factory.isAppNotifyHookOf
import com.fankes.coloros.notify.param.factory.putAppNotifyHookAllOf
import com.fankes.coloros.notify.param.factory.putAppNotifyHookOf
import com.fankes.coloros.notify.ui.activity.base.BaseActivity
import com.fankes.coloros.notify.utils.factory.*
import com.fankes.coloros.notify.utils.factory.addOnBackPressedEvent
import com.fankes.coloros.notify.utils.factory.bindAdapter
import com.fankes.coloros.notify.utils.factory.callOnBackPressed
import com.fankes.coloros.notify.utils.factory.colorOf
import com.fankes.coloros.notify.utils.factory.copyToClipboard
import com.fankes.coloros.notify.utils.factory.navigate
import com.fankes.coloros.notify.utils.factory.openBrowser
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.snake
import com.fankes.coloros.notify.utils.factory.toast
import com.fankes.coloros.notify.utils.tool.IconRuleManagerTool
import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
import com.highcapable.yukihookapi.YukiHookAPI
class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
@@ -60,18 +66,18 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
private var iconAllDatas = ArrayList<IconDataBean>()
override fun onCreate() {
/** 检查激活状态 */
if (isXposedModuleActive.not()) {
/** 检查激活和启用状态 */
if (YukiHookAPI.Status.isXposedModuleActive.not() || ConfigData.isEnableModule.not()) {
showDialog {
title = "模块没有激活"
msg = "模块没有激活,你无法使用这里的功能,请先激活模块。"
title = "模块不可用"
msg = "模块没有激活或已被停用,你无法使用这里的功能,请先激活或启用模块。"
confirmButton(text = "我知道了") { finish() }
noCancelable()
}
return
}
/** 返回按钮点击事件 */
binding.titleBackIcon.setOnClickListener { onBackPressed() }
binding.titleBackIcon.setOnClickListener { callOnBackPressed() }
/** 刷新适配器结果相关 */
refreshAdapterResult()
/** 设置上下按钮点击事件 */
@@ -85,9 +91,9 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
}
/** 设置过滤按钮点击事件 */
binding.configTitleFilter.setOnClickListener {
showDialog {
showDialog<DiaIconFilterBinding> {
title = "按条件过滤"
val editText = bind<DiaIconFilterBinding>().diaIconFilterInputEdit.apply {
binding.iconFiltersEdit.apply {
requestFocus()
invalidate()
if (filterText.isNotBlank()) {
@@ -96,8 +102,8 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
}
}
confirmButton {
if (editText.text.toString().isNotBlank()) {
filterText = editText.text.toString().trim()
if (binding.iconFiltersEdit.text.toString().isNotBlank()) {
filterText = binding.iconFiltersEdit.text.toString().trim()
refreshAdapterResult()
} else {
toast(msg = "条件不能为空")
@@ -116,49 +122,46 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
binding.configTitleSync.setOnClickListener { onStartRefresh() }
/** 设置列表元素和 Adapter */
binding.configListView.apply {
adapter = object : BaseAdapter() {
override fun getCount() = iconDatas.size
override fun getItem(position: Int) = iconDatas[position]
override fun getItemId(position: Int) = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var cView = convertView
val holder: AdapterConfigBinding
if (convertView == null) {
holder = AdapterConfigBinding.inflate(LayoutInflater.from(context))
cView = holder.root
cView.tag = holder
} else holder = convertView.tag as AdapterConfigBinding
getItem(position).also {
holder.adpAppIcon.setImageBitmap(it.iconBitmap)
(if (it.iconColor != 0) it.iconColor else resources.getColor(R.color.colorTextGray)).also { color ->
holder.adpAppIcon.setColorFilter(color)
holder.adpAppName.setTextColor(color)
bindAdapter {
onBindDatas { iconDatas }
onBindViews<AdapterConfigBinding> { binding, position ->
iconDatas[position].also { bean ->
binding.adpAppIcon.setImageBitmap(bean.iconBitmap)
(if (bean.iconColor != 0) bean.iconColor else resources.colorOf(R.color.colorTextGray)).also { color ->
binding.adpAppIcon.setColorFilter(color)
binding.adpAppName.setTextColor(color)
}
holder.adpAppName.text = it.appName
holder.adpAppPkgName.text = it.packageName
holder.adpCbrName.text = "贡献者:" + it.contributorName
isAppNotifyHookOf(it).also { e ->
holder.adpAppOpenSwitch.isChecked = e
holder.adpAppAllSwitch.isEnabled = e
binding.adpAppName.text = bean.appName
binding.adpAppPkgName.text = bean.packageName
binding.adpCbrName.text = "贡献者:" + bean.contributorName
isAppNotifyHookOf(bean).also { e ->
binding.adpAppOpenSwitch.isChecked = e
binding.adpAppAllSwitch.isEnabled = e
}
holder.adpAppOpenSwitch.setOnCheckedChangeListener { btn, b ->
binding.adpAppOpenSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
putAppNotifyHookOf(it, b)
holder.adpAppAllSwitch.isEnabled = b
putAppNotifyHookOf(bean, b)
binding.adpAppAllSwitch.isEnabled = b
SystemUITool.refreshSystemUI(context = this@ConfigureActivity)
}
holder.adpAppAllSwitch.isChecked = isAppNotifyHookAllOf(it)
holder.adpAppAllSwitch.setOnCheckedChangeListener { btn, b ->
binding.adpAppAllSwitch.isChecked = isAppNotifyHookAllOf(bean)
binding.adpAppAllSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
putAppNotifyHookAllOf(it, b)
SystemUITool.refreshSystemUI(context = this@ConfigureActivity)
fun saveState() {
putAppNotifyHookAllOf(bean, b)
SystemUITool.refreshSystemUI(context = this@ConfigureActivity)
}
if (b) showDialog {
title = "全部替换"
msg = "此功能仅针对严重不遵守规范的 APP 通知图标才需要开启例如APP 推送通知后无法识别出现的黑白块图标。\n\n" +
"此功能在一般情况下请保持关闭并跟随在线规则的配置,并不要随意改变此配置," +
"开启后 APP 的通知图标可能会被规则破坏,你确定还要开启吗?"
confirmButton { saveState() }
cancelButton { btn.isChecked = btn.isChecked.not() }
noCancelable()
} else saveState()
}
}
return cView!!
}
}.apply {
setOnItemLongClickListener { _, _, p, _ ->
@@ -176,7 +179,13 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
}
/** 设置点击事件 */
binding.configCbrButton.setOnClickListener {
openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md")
showDialog {
title = "感谢你的贡献"
msg = "通知图标优化名单需要大家的共同维护才能得以完善,请选择你的贡献方式。"
confirmButton(text = "贡献规则") { openBrowser(IconRuleManagerTool.RULES_CONTRIBUTING_URL) }
cancelButton(text = "请求适配") { openBrowser(IconRuleManagerTool.RULES_FEEDBACK_URL) }
neutralButton(text = "暂时不用")
}
}
/** 装载数据 */
mockLocalData()
@@ -188,30 +197,56 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
val pkgName = intent?.getStringExtra("pkgName") ?: ""
title = "新安装应用通知图标适配"
msg = "你已安装 $appName($pkgName)\n\n" +
"此应用未在通知优化名单中发现适配数据,若此应用发送的通知为彩色图标," +
"可随时点击本页面下方的“贡献通知图标优化名单”按钮提交贡献或请求适配。\n\n" +
"若你已知晓此应用会遵守原生通知图标规范,可忽略此提示。\n\n" +
"你可以现在立即同步适配列表,以获取最新的适配数据。"
"此应用未在通知优化名单中发现适配数据,若此应用发送的通知为彩色图标," +
"可随时点击本页面下方的“贡献通知图标优化名单”按钮提交贡献或请求适配。\n\n" +
"若你已知晓此应用会遵守原生通知图标规范,可忽略此提示。\n\n" +
"你可以现在立即同步适配列表,以获取最新的适配数据。"
confirmButton(text = "同步列表") { onStartRefresh() }
cancelButton(text = "复制名称+包名") { copyToClipboard(content = "$appName($pkgName)") }
neutralButton(text = "取消")
noCancelable()
}
intent?.getBooleanExtra("isDirectUpdate", false) == true -> onStartRefresh(isByHand = false)
intent?.getBooleanExtra("isShowUpdDialog", true) == true -> onStartRefresh()
}
/** 清除数据 */
intent?.apply {
removeExtra("isNewAppSupport")
removeExtra("isDirectUpdate")
removeExtra("isShowUpdDialog")
}
/** 设置返回监听事件 */
addOnBackPressedEvent {
if (MainActivity.isActivityLive.not())
showDialog {
title = "提示"
msg = "要返回模块主页吗?"
confirmButton {
releaseEventAndBack()
navigate<MainActivity>()
}
cancelButton { releaseEventAndBack() }
}
else releaseEventAndBack()
}
}
/** 开始手动同步 */
private fun onStartRefresh() =
IconRuleManagerTool.syncByHand(context = this) {
filterText = ""
mockLocalData()
}
/**
* 开始同步
* @param isByHand 是否手动同步 - 默认是
*/
private fun onStartRefresh(isByHand: Boolean = true) {
if (isByHand)
IconRuleManagerTool.syncByHand(context = this) {
filterText = ""
mockLocalData()
}
else
IconRuleManagerTool.sync(context = this) {
filterText = ""
mockLocalData()
}
}
/** 装载或刷新本地数据 */
private fun mockLocalData() {
@@ -224,7 +259,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
onChanged?.invoke()
binding.configTitleCountText.text =
if (filterText.isBlank()) "已适配 ${iconDatas.size} 个 APP 的通知图标"
else "${filterText}” 匹配到 ${iconDatas.size} 个结果"
else "$filterText” 匹配到 ${iconDatas.size} 个结果"
binding.configListNoDataView.apply {
text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~"
isVisible = iconDatas.isEmpty()
@@ -240,18 +275,4 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
else iconAllDatas.filter {
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
}
override fun onBackPressed() {
if (MainActivity.isActivityLive.not())
showDialog {
title = "提示"
msg = "要返回模块主页吗?"
confirmButton {
super.onBackPressed()
navigate<MainActivity>()
}
cancelButton { super.onBackPressed() }
}
else super.onBackPressed()
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,29 +18,37 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/2/26.
* This file is created by fankes on 2022/2/26.
*/
@file:Suppress("SetTextI18n")
package com.fankes.coloros.notify.ui.activity
import android.content.ComponentName
import android.content.pm.PackageManager
import 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
import com.fankes.coloros.notify.data.DataConst
import com.fankes.coloros.notify.const.ModuleVersion
import com.fankes.coloros.notify.data.ConfigData
import com.fankes.coloros.notify.data.factory.bind
import com.fankes.coloros.notify.databinding.ActivityMainBinding
import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.ui.activity.base.BaseActivity
import com.fankes.coloros.notify.utils.factory.*
import com.fankes.coloros.notify.utils.factory.androidVersionCodeName
import com.fankes.coloros.notify.utils.factory.colorOSFullVersion
import com.fankes.coloros.notify.utils.factory.colorOSNumberVersion
import com.fankes.coloros.notify.utils.factory.hideOrShowLauncherIcon
import com.fankes.coloros.notify.utils.factory.isLauncherIconShowing
import com.fankes.coloros.notify.utils.factory.isNotColorOS
import com.fankes.coloros.notify.utils.factory.isNotNoificationEnabled
import com.fankes.coloros.notify.utils.factory.navigate
import com.fankes.coloros.notify.utils.factory.openBrowser
import com.fankes.coloros.notify.utils.factory.openNotifySetting
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.showTimePicker
import com.fankes.coloros.notify.utils.tool.GithubReleaseTool
import com.fankes.coloros.notify.utils.tool.I18nWarnTool
import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
import com.fankes.projectpromote.ProjectPromote
import com.highcapable.yukihookapi.YukiHookAPI
class MainActivity : BaseActivity<ActivityMainBinding>() {
@@ -49,27 +57,18 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
/** 窗口是否启动 */
internal var isActivityLive = false
/** 模块版本 */
private const val moduleVersion = BuildConfig.VERSION_NAME
/** 模块是否可用 */
internal var isModuleRegular = false
/** 预发布的版本标识 */
private const val pendingFlag = ""
/** 模块是否有效 */
internal var isModuleValied = false
}
/** 模块是否可用 */
private var isModuleRegular = false
/** 模块是否有效 */
private var isModuleValied = false
override fun onCreate() {
/** 设置可用性 */
isActivityLive = true
/** 设置文本 */
binding.mainTextVersion.text = "模块版本:$moduleVersion $pendingFlag"
binding.mainTextColorOsVersion.text = "系统版本:$colorOSFullVersion"
/** 检查更新 */
GithubReleaseTool.checkingForUpdate(context = this, moduleVersion) { version, function ->
GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
binding.mainTextReleaseVersion.apply {
text = "点击更新 $version"
isVisible = true
@@ -81,23 +80,26 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
isNotColorOS ->
showDialog {
title = "不是 ColorOS 系统"
msg = "此模块专为 ColorOS 系统打造,当前无法识别你的系统为 ColorOS所以模块无法工作。\n" +
"如有问题请联系 酷安 @星夜不荟"
confirmButton(text = "退出") { finish() }
msg = "此模块专为 ColorOS 系统打造,当前无法识别你的系统为 ColorOS所以模块无法工作。"
confirmButton(text = "查看支持的模块") {
openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt")
finish()
}
cancelButton(text = "退出") { finish() }
noCancelable()
}
/** 判断是否 Hook */
isXposedModuleActive -> {
if (IconPackParams(context = this).iconDatas.isEmpty() && modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX))
YukiHookAPI.Status.isXposedModuleActive -> {
if (IconPackParams(context = this).iconDatas.isEmpty() && ConfigData.isEnableNotifyIconFix)
showDialog {
title = "配置通知图标优化名单"
msg = "模块需要获取在线规则以更新“通知图标优化名单”,它现在是空的,这看起来是你第一次使用模块,请首先进行配置才可以使用相关功能。\n" +
"你可以随时在本页面下方找到“配置通知图标优化名单”手动前往。"
"你可以随时在本页面下方找到“配置通知图标优化名单”手动前往。"
confirmButton(text = "前往") { navigate<ConfigureActivity>() }
cancelButton()
noCancelable()
}
if (isNotNoificationEnabled && modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX))
if (isNotNoificationEnabled && ConfigData.isEnableNotifyIconFix)
showDialog {
title = "模块的通知权限已关闭"
msg = "请开启通知权限,以确保你能收到通知优化图标在线规则的更新。"
@@ -105,140 +107,226 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
cancelButton()
noCancelable()
}
/** 推广、恰饭 */
ProjectPromote.show(activity = this, ModuleVersion.toString())
}
else ->
showDialog {
title = "模块没有激活"
msg = "检测到模块没有激活,模块需要 Xposed 环境依赖," +
"同时需要系统拥有 Root 权限," +
"请自行查看本页面使用帮助与说明第二条。\n" +
"由于需要修改系统应用达到效果,模块不支持太极阴、应用转生。"
"同时需要系统拥有 Root 权限," +
"请自行查看本页面使用帮助与说明第二条。\n" +
"由于需要修改系统应用达到效果,模块不支持太极阴、应用转生。"
confirmButton(text = "我知道了")
noCancelable()
}
}
var notifyIconAutoSyncTime = modulePrefs.get(DataConst.NOTIFY_ICON_FIX_AUTO_TIME)
binding.devNotifyConfigItem.isVisible = modulePrefs.get(DataConst.ENABLE_MODULE)
binding.a12StyleConfigItem.isVisible = modulePrefs.get(DataConst.ENABLE_MODULE)
binding.notifyIconConfigItem.isVisible = modulePrefs.get(DataConst.ENABLE_MODULE)
binding.notifyIconFixButton.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
binding.notifyIconFixNotifyItem.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
binding.notifyIconAutoSyncItem.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
binding.notifyIconAutoSyncChildItem.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_AUTO)
binding.notifyPanelConfigSeekbar.isVisible = modulePrefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA)
binding.notifyPanelConfigTextPanel.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.moduleEnableSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_MODULE)
binding.moduleEnableLogSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_MODULE_LOG)
binding.hideIconInLauncherSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_HIDE_ICON)
binding.notifyIconFixSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX)
binding.notifyIconFixNotifySwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY)
binding.notifyIconAutoSyncSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_AUTO)
binding.notifyPanelConfigSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_PANEL_ALPHA)
binding.notifyPanelConfigSeekbar.progress = modulePrefs.get(DataConst.NOTIFY_PANEL_ALPHA)
binding.notifyPanelConfigText.text = modulePrefs.get(DataConst.NOTIFY_PANEL_ALPHA).toString()
binding.notifyIconAutoSyncText.text = notifyIconAutoSyncTime
binding.moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.ENABLE_MODULE, b)
binding.moduleEnableLogSwitch.isVisible = b
binding.notifyIconConfigItem.isVisible = b
binding.devNotifyConfigItem.isVisible = b
binding.a12StyleConfigItem.isVisible = b
SystemUITool.showNeedRestartSnake(context = this)
}
binding.moduleEnableLogSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.ENABLE_MODULE_LOG, b)
SystemUITool.showNeedRestartSnake(context = this)
}
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.ENABLE_HIDE_ICON, b)
packageManager.setComponentEnabledSetting(
ComponentName(packageName, "com.fankes.coloros.notify.Home"),
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
)
}
binding.notifyIconFixSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX, b)
binding.notifyIconFixButton.isVisible = b
binding.notifyIconFixNotifyItem.isVisible = b
binding.notifyIconAutoSyncItem.isVisible = b
SystemUITool.refreshSystemUI(context = this)
}
binding.notifyIconFixNotifySwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY, b)
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
}
binding.devNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.REMOVE_DEV_NOTIFY, b)
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
}
binding.crcpNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.REMOVE_CHANGECP_NOTIFY, b)
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
}
binding.dndNotifyConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.REMOVE_DNDALERT_NOTIFY, b)
SystemUITool.refreshSystemUI(context = this, isRefreshCacheOnly = true)
}
binding.a12StyleConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.ENABLE_ANDROID12_STYLE, b)
SystemUITool.refreshSystemUI(context = this)
}
binding.notifyPanelConfigSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
modulePrefs.put(DataConst.ENABLE_NOTIFY_PANEL_ALPHA, b)
binding.notifyPanelConfigTextPanel.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.toString()
I18nWarnTool.checkingOrShowing(context = this)
binding.mainTextVersion.text = "模块版本:${ModuleVersion.NAME}"
/** 设置 CI 自动构建标识 */
if (ModuleVersion.isCiMode)
binding.mainTextReleaseVersion.apply {
text = "CI ${ModuleVersion.GITHUB_COMMIT_ID}"
isVisible = true
setOnClickListener {
showDialog {
title = "CI 自动构建说明"
msg = """
你正在使用的是 CI 自动构建版本Commit ID 为 ${ModuleVersion.GITHUB_COMMIT_ID}
它是由代码提交后自动触发并构建、自动编译发布的,并未经任何稳定性测试,使用风险自负。
""".trimIndent()
confirmButton(text = "我知道了")
noCancelable()
}
}
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
modulePrefs.put(DataConst.NOTIFY_PANEL_ALPHA, seekBar.progress)
binding.mainTextColorOsVersion.text = "系统版本:[$androidVersionCodeName] $colorOSFullVersion"
/** 媒体通知自动展开仅支持 12.1 - 旧版本适配过于复杂已放弃 */
if (colorOSNumberVersion != "V12.1") {
binding.notifyMediaPanelAutoExpSwitch.isVisible = false
binding.notifyMediaPanelAutoExpText.isVisible = false
}
/** 通知面板背景透明度功能仅支持 ColorOS 12、12.1、13、13.1 */
binding.notifyPanelConfigItem.isVisible = colorOSNumberVersion.let { it == "V12" || it == "V12.1" || it == "V13" || it == "V13.1" }
binding.notifyIconAutoSyncText.text = ConfigData.notifyIconFixAutoTime
binding.moduleEnableSwitch.bind(ConfigData.ENABLE_MODULE) {
onInitialize {
binding.moduleEnableLogItem.isVisible = it
binding.expAllDebugLogButton.isVisible = it && ConfigData.isEnableModuleLog
binding.notifyIconConfigItem.isVisible = it
binding.devNotifyConfigItem.isVisible = it
binding.notifyStyleConfigItem.isVisible = it
}
onChanged {
reinitialize()
refreshModuleStatus()
SystemUITool.showNeedRestartSnake(context = this@MainActivity)
}
}
binding.moduleEnableLogSwitch.bind(ConfigData.ENABLE_MODULE_LOG) {
onInitialize { binding.expAllDebugLogButton.isVisible = it && ConfigData.isEnableModule }
onChanged {
reinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true)
}
}
binding.devNotifyConfigSwitch.bind(ConfigData.ENABLE_REMOVE_DEV_NOTIFY) {
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
}
binding.crcpNotifyConfigSwitch.bind(ConfigData.ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY) {
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
}
binding.dndNotifyConfigSwitch.bind(ConfigData.ENABLE_REMOVE_DND_ALERT_NOTIFY) {
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
}
binding.colorIconCompatSwitch.bind(ConfigData.ENABLE_COLOR_ICON_COMPAT) {
isAutoApplyChanges = false
onChanged {
/** 应用更改并刷新系统界面 */
fun applyChangesAndRefresh() {
applyChanges()
SystemUITool.refreshSystemUI(context = this@MainActivity)
}
if (it) showDialog {
title = "启用兼容模式"
msg = "启用兼容模式可修复部分系统版本可能出现无法判定通知图标反色的问题," +
"但是这也可能会导致新的问题,一般情况下不建议开启,确定要继续吗?\n\n" +
"如果系统界面刷新后通知图标颜色发生错误,请尝试重启一次系统界面。"
confirmButton { applyChangesAndRefresh() }
cancelButton { cancelChanges() }
noCancelable()
} else applyChangesAndRefresh()
}
}
binding.md3StyleConfigSwitch.bind(ConfigData.ENABLE_MD3_NOTIFY_ICON_STYLE) {
onInitialize { binding.notifyIconCustomCornerItem.isVisible = it && ConfigData.isEnableNotifyIconForceAppIcon.not() }
onChanged {
reinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
})
}
binding.notifyIconForceSystemColorSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR) {
isAutoApplyChanges = false
onChanged {
/** 应用更改并刷新系统界面 */
fun applyChangesAndRefresh() {
applyChangesAndReinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity)
}
if (it) showDialog {
title = "破坏性功能警告"
msg = "开启这个功能后,任何通知栏中的通知图标都会忽略图标自身的着色属性,全部使用系统默认颜色 (系统提供的统一色调) 着色。\n\n" +
"此功能仅面向一些追求图标美观度的用户,我们不推荐开启这个功能,且发生任何 BUG 都不会去修复,仍然继续开启吗?"
confirmButton { applyChangesAndRefresh() }
cancelButton { cancelChanges() }
noCancelable()
} else applyChangesAndRefresh()
}
}
binding.notifyIconForceAppIconSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FORCE_APP_ICON) {
isAutoApplyChanges = false
onInitialize {
binding.notifyIconForceSystemColorItem.isVisible = it.not()
binding.notifyIconCustomCornerItem.isVisible = it.not() && ConfigData.isEnableMd3NotifyIconStyle
}
onChanged {
/** 应用更改并刷新系统界面 */
fun applyChangesAndRefresh() {
applyChangesAndReinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity)
}
if (it) showDialog {
title = "破坏性功能警告"
msg = "开启这个功能后,任何通知栏中的通知图标都会被强制替换为当前推送通知的 APP 的图标," +
"某些系统级别的 APP 通知图标可能会显示异常或发生图标丢失。\n\n" +
"此功能仅面向一些追求图标美观度的用户,我们不推荐开启这个功能,且发生任何 BUG 都不会去修复,仍然继续开启吗?"
confirmButton { applyChangesAndRefresh() }
cancelButton { cancelChanges() }
noCancelable()
} else applyChangesAndRefresh()
}
}
binding.notifyPanelConfigSwitch.bind(ConfigData.ENABLE_NOTIFY_PANEL_ALPHA) {
onInitialize {
binding.notifyPanelConfigTextPanel.isVisible = it
binding.notifyPanelConfigWarnPanel.isVisible = it
binding.notifyPanelConfigSeekbar.isVisible = it
}
onChanged {
reinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity)
}
}
binding.notifyMediaPanelAutoExpSwitch.bind(ConfigData.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP) {
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
}
binding.notifyIconFixSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX) {
onInitialize {
binding.notifyIconFixButton.isVisible = it
binding.notifyIconFixPlaceholderItem.isVisible = it
binding.notifyIconFixNotifyItem.isVisible = it
binding.notifyIconAutoSyncItem.isVisible = it
}
onChanged {
reinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity)
}
}
binding.notifyIconFixPlaceholderSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER) {
isAutoApplyChanges = false
onChanged {
/** 应用更改并刷新系统界面 */
fun applyChangesAndRefresh() {
applyChanges()
SystemUITool.refreshSystemUI(context = this@MainActivity)
}
if (it) showDialog {
title = "注意"
msg = "开启这个功能后,当发现未适配的彩色通知图标时," +
"状态栏中显示的通知图标将会使用预置的占位符图标进行修补," +
"通知栏中显示的通知图标保持原始图标不变。\n\n" +
"此功能的作用仅为临时修复破坏规范的通知图标,仍然继续开启吗?"
confirmButton { applyChangesAndRefresh() }
cancelButton { cancelChanges() }
noCancelable()
} else applyChangesAndRefresh()
}
}
binding.notifyIconFixNotifySwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX_NOTIFY) {
onChanged { SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true) }
}
binding.notifyIconAutoSyncSwitch.bind(ConfigData.ENABLE_NOTIFY_ICON_FIX_AUTO) {
onInitialize { binding.notifyIconAutoSyncChildItem.isVisible = it }
onChanged {
reinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true)
}
}
binding.notifyPanelConfigSeekbar.bind(ConfigData.NOTIFY_PANEL_ALPHA_LEVEL, binding.notifyPanelConfigText, suffix = "%") {
SystemUITool.refreshSystemUI(context = this)
}
binding.notifyIconCustomCornerSeekbar.bind(ConfigData.NOTIFY_ICON_CORNER_SIZE, binding.notifyIconCustomCornerText, suffix = " dp") {
SystemUITool.refreshSystemUI(context = this)
}
/** 导出全部日志按钮点击事件 */
binding.expAllDebugLogButton.setOnClickListener { SystemUITool.obtainAndExportDebugLogs(context = this) }
/** 通知图标优化名单按钮点击事件 */
binding.notifyIconFixButton.setOnClickListener { navigate<ConfigureActivity>() }
/** 自动更新在线规则修改时间按钮点击事件 */
binding.notifyIconAutoSyncButton.setOnClickListener {
showTimePicker(notifyIconAutoSyncTime) {
showTimePicker(ConfigData.notifyIconFixAutoTime) {
showDialog {
title = "每天 $it 自动更新"
msg = "设置保存后将在每天 $it 自动同步名单到最新云端数据,若数据已是最新则不会显示任何提示,否则会发送一条通知。\n\n" +
"请确保:\n\n" +
"1.模块没有被禁止前台以及后台联网权限\n" +
"2.模块没有被禁止被其它 APP 关联唤醒\n" +
"3.模块的系统通知权限已开启\n\n" +
"模块无需保持在后台运行,到达同步时间后会自动启动,如果到达时间后模块正在运行则会自动取消本次计划任务。"
"请确保:\n\n" +
"1.模块没有被禁止前台以及后台联网权限\n" +
"2.模块没有被禁止被其它 APP 关联唤醒\n" +
"3.模块的系统通知权限已开启\n\n" +
"模块无需保持在后台运行,到达同步时间后会自动启动,如果到达时间后模块正在运行则会自动取消本次计划任务。"
confirmButton(text = "保存设置") {
notifyIconAutoSyncTime = it
binding.notifyIconAutoSyncText.text = it
modulePrefs.put(DataConst.NOTIFY_ICON_FIX_AUTO_TIME, it)
ConfigData.notifyIconFixAutoTime = it
this@MainActivity.binding.notifyIconAutoSyncText.text = it
SystemUITool.refreshSystemUI(context, isRefreshCacheOnly = true)
}
cancelButton()
@@ -254,42 +342,50 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.linkWithFollowMe.setOnClickListener {
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
}
/** 设置桌面图标显示隐藏 */
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
hideOrShowLauncherIcon(b)
}
/** 注册导出调试日志启动器 */
SystemUITool.registerExportDebugLogsLauncher(activity = this)
}
/** 刷新模块状态 */
private fun refreshModuleStatus() {
binding.mainLinStatus.setBackgroundResource(
when {
isXposedModuleActive && (isModuleRegular.not() || isModuleValied.not()) -> R.drawable.bg_yellow_round
isXposedModuleActive -> R.drawable.bg_green_round
YukiHookAPI.Status.isXposedModuleActive &&
(isModuleRegular.not() || isModuleValied.not() || ConfigData.isEnableModule.not()) -> R.drawable.bg_yellow_round
YukiHookAPI.Status.isXposedModuleActive -> R.drawable.bg_green_round
else -> R.drawable.bg_dark_round
}
)
binding.mainImgStatus.setImageResource(
when {
isXposedModuleActive -> R.mipmap.ic_success
else -> R.mipmap.ic_warn
YukiHookAPI.Status.isXposedModuleActive && ConfigData.isEnableModule -> R.drawable.ic_success
else -> R.drawable.ic_warn
}
)
binding.mainTextStatus.text =
when {
isXposedModuleActive && isModuleRegular.not() && modulePrefs.get(DataConst.ENABLE_MODULE).not() -> "模块已停用"
isXposedModuleActive && isModuleRegular.not() -> "模块已激活,请重启系统界面"
isXposedModuleActive && isModuleValied.not() -> "模块已更新,请重启系统界面"
isXposedModuleActive -> "模块激活"
else -> "模块未激活"
}
binding.mainTextApiWay.isVisible = isXposedModuleActive
binding.mainTextApiWay.text = "Activated by ${YukiHookModuleStatus.executorName} API ${YukiHookModuleStatus.executorVersion}"
binding.mainTextStatus.text = when {
YukiHookAPI.Status.isXposedModuleActive && ConfigData.isEnableModule.not() -> "模块已停用"
YukiHookAPI.Status.isXposedModuleActive && isModuleRegular.not() -> "模块已激活,请重启系统界面"
YukiHookAPI.Status.isXposedModuleActive && isModuleValied.not() -> "模块已更新,请重启系统界面"
YukiHookAPI.Status.isXposedModuleActive -> "模块已激活"
else -> "模块激活"
}
binding.mainTextApiWay.isVisible = YukiHookAPI.Status.isXposedModuleActive
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
}
override fun onResume() {
super.onResume()
/** 刷新模块状态 */
refreshModuleStatus()
/** 发送广播检查模块激活状态 */
SystemUITool.checkingActivated(context = this) { isRegular, isValied ->
isModuleRegular = isRegular
/** 检查模块激活状态 */
SystemUITool.checkingActivated(context = this) { isValied ->
isModuleRegular = true
isModuleValied = isValied
refreshModuleStatus()
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,7 +18,7 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/3/26.
* This file is created by fankes on 2022/3/26.
*/
@file:Suppress("DEPRECATION")
@@ -32,7 +32,6 @@ 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() {
@@ -43,15 +42,13 @@ class NotifyIconRuleUpdateActivity : Activity() {
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
window?.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window?.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
/** 注册 */
SystemUITool.register(context = this)
/** 检测运行状态 */
if (BaseActivity.isMainThreadRunning) {
finish()
return
}
/** 拉取云端数据 */
IconRuleManagerTool.sync(appContext) {
IconRuleManagerTool.sync(context = this) {
/** 刷新系统界面 */
SystemUITool.refreshSystemUI()
/** 结束当前窗口 */
@@ -60,10 +57,4 @@ class NotifyIconRuleUpdateActivity : Activity() {
/** 切换到后台 */
moveTaskToBack(true)
}
override fun onDestroy() {
super.onDestroy()
/** 解除注册 */
SystemUITool.unregister(context = this)
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,22 +18,22 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/30.
* This file is created by fankes on 2022/1/30.
*/
@file:Suppress("UNCHECKED_CAST")
package com.fankes.coloros.notify.ui.activity.base
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding
import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode
import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.gyf.immersionbar.ktx.immersionBar
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
import java.lang.reflect.ParameterizedType
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.kavaref.extension.toClassOrNull
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -49,38 +49,30 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
isMainThreadRunning = true
javaClass.genericSuperclass.also { type ->
if (type is ParameterizedType) {
binding = (type.actualTypeArguments[0] as Class<VB>).method {
name = "inflate"
param(LayoutInflaterClass)
}.get().invoke<VB>(layoutInflater) ?: error("binding failed")
setContentView(binding.root)
} else error("binding but got wrong type")
}
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate"
parameters(LayoutInflater::class)
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root)
/** 隐藏系统的标题栏 */
supportActionBar?.hide()
/** 初始化沉浸状态栏 */
immersionBar {
statusBarColor(R.color.colorThemeBackground)
autoDarkModeEnable(true)
statusBarDarkFont(isNotSystemInDarkMode)
navigationBarColor(R.color.colorThemeBackground)
navigationBarDarkIcon(isNotSystemInDarkMode)
fitsSystemWindows(true)
WindowCompat.getInsetsController(window, window.decorView).apply {
isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode
}
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it
window?.navigationBarColor = it
window?.navigationBarDividerColor = it
}
/** 注册 */
SystemUITool.register(context = this)
/** 装载子类 */
onCreate()
}
/** 回调 [onCreate] 方法 */
abstract fun onCreate()
override fun onDestroy() {
super.onDestroy()
/** 解除注册 */
SystemUITool.unregister(context = this)
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,11 +18,11 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
* This file is created by fankes on 2022/1/8.
*/
@file:Suppress("SameParameterValue")
package com.fankes.coloros.notify.ui.view
package com.fankes.coloros.notify.ui.widget
import android.content.Context
import android.content.res.ColorStateList
@@ -30,9 +30,9 @@ import android.graphics.Color
import android.text.TextUtils
import android.util.AttributeSet
import androidx.appcompat.widget.SwitchCompat
import com.fankes.coloros.notify.utils.drawable.drawabletoolbox.DrawableBuilder
import com.fankes.coloros.notify.utils.factory.dp
import com.fankes.coloros.notify.utils.factory.isSystemInDarkMode
import top.defaults.drawabletoolbox.DrawableBuilder
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {

View File

@@ -1,293 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
@file:Suppress("SameParameterValue")
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.annotation.SuppressLint
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.RippleDrawable
import android.graphics.drawable.RotateDrawable
import android.os.Build
import java.lang.reflect.Field
import java.lang.reflect.Method
private val gradientState = resolveGradientState()
private fun resolveGradientState(): Class<*> {
val classes = GradientDrawable::class.java.declaredClasses
for (singleClass in classes) {
if (singleClass.simpleName == "GradientState") return singleClass
}
throw RuntimeException("GradientState could not be found in thisAny GradientDrawable implementation")
}
private val rotateState = resolveRotateState()
private fun resolveRotateState(): Class<*> {
val classes = RotateDrawable::class.java.declaredClasses
for (singleClass in classes) {
if (singleClass.simpleName == "RotateState") return singleClass
}
throw RuntimeException("RotateState could not be found in thisAny RotateDrawable implementation")
}
@Throws(SecurityException::class, NoSuchFieldException::class)
private fun resolveField(source: Class<*>, fieldName: String): Field {
val field = source.getDeclaredField(fieldName)
field.isAccessible = true
return field
}
@Throws(SecurityException::class, NoSuchMethodException::class)
private fun resolveMethod(
source: Class<*>,
methodName: String,
vararg parameterTypes: Class<*>
): Method {
val method = source.getDeclaredMethod(methodName, *parameterTypes)
method.isAccessible = true
return method
}
fun setInnerRadius(drawable: GradientDrawable, value: Int) {
try {
val innerRadius = resolveField(gradientState, "mInnerRadius")
innerRadius.setInt(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
fun setInnerRadiusRatio(drawable: GradientDrawable, value: Float) {
try {
val innerRadius = resolveField(gradientState, "mInnerRadiusRatio")
innerRadius.setFloat(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
fun setThickness(drawable: GradientDrawable, value: Int) {
try {
val innerRadius = resolveField(gradientState, "mThickness")
innerRadius.setInt(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
fun setThicknessRatio(drawable: GradientDrawable, value: Float) {
try {
val innerRadius = resolveField(gradientState, "mThicknessRatio")
innerRadius.setFloat(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
fun setUseLevelForShape(drawable: GradientDrawable, value: Boolean) {
try {
val useLevelForShape = resolveField(gradientState, "mUseLevelForShape")
useLevelForShape.setBoolean(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
@SuppressLint("ObsoleteSdkInt")
fun setOrientation(drawable: GradientDrawable, value: GradientDrawable.Orientation) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
drawable.orientation = value
} else {
try {
val orientation = resolveField(gradientState, "mOrientation")
orientation.set(drawable.constantState, value)
val rectIdDirty = resolveField(GradientDrawable::class.java, "mRectIsDirty")
rectIdDirty.setBoolean(drawable, true)
drawable.invalidateSelf()
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}
@SuppressLint("ObsoleteSdkInt")
fun setColors(drawable: GradientDrawable, value: IntArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
drawable.colors = value
} else {
try {
val colors = resolveField(gradientState, "mColors")
colors.set(drawable.constantState, value)
drawable.invalidateSelf()
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}
fun setGradientRadiusType(drawable: GradientDrawable, value: Int) {
try {
val type = resolveField(gradientState, "mGradientRadiusType")
type.setInt(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
fun setGradientRadius(drawable: GradientDrawable, value: Float) {
try {
val gradientRadius = resolveField(gradientState, "mGradientRadius")
gradientRadius.setFloat(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
fun setStrokeColor(drawable: GradientDrawable, value: Int) {
try {
val type = resolveField(gradientState, "mStrokeColor")
type.setInt(drawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
fun setDrawable(rotateDrawable: RotateDrawable, drawable: Drawable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
rotateDrawable.drawable = drawable
} else {
try {
val drawableField = resolveField(rotateState, "mDrawable")
val stateField = resolveField(RotateDrawable::class.java, "mState")
drawableField.set(stateField.get(rotateDrawable), drawable)
drawable.callback = rotateDrawable
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}
fun setPivotX(rotateDrawable: RotateDrawable, value: Float) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
rotateDrawable.pivotX = value
} else {
try {
val pivotXField = resolveField(rotateState, "mPivotX")
pivotXField.setFloat(rotateDrawable.constantState, value)
val pivotXRelField = resolveField(rotateState, "mPivotXRel")
pivotXRelField.setBoolean(rotateDrawable.constantState, true)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}
fun setPivotY(rotateDrawable: RotateDrawable, value: Float) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
rotateDrawable.pivotY = value
} else {
try {
val pivotYField = resolveField(rotateState, "mPivotY")
pivotYField.setFloat(rotateDrawable.constantState, value)
val pivotYRelField = resolveField(rotateState, "mPivotYRel")
pivotYRelField.setBoolean(rotateDrawable.constantState, true)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}
fun setFromDegrees(rotateDrawable: RotateDrawable, value: Float) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
rotateDrawable.fromDegrees = value
} else {
try {
val fromDegreesField = resolveField(rotateState, "mFromDegrees")
fromDegreesField.setFloat(rotateDrawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}
fun setToDegrees(rotateDrawable: RotateDrawable, value: Float) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
rotateDrawable.toDegrees = value
} else {
try {
val toDegreesField = resolveField(rotateState, "mToDegrees")
toDegreesField.setFloat(rotateDrawable.constantState, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}
fun setRadius(rippleDrawable: RippleDrawable, value: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
rippleDrawable.radius = value
} else {
try {
val setRadiusMethod =
resolveMethod(RippleDrawable::class.java, "setMaxRadius", Int::class.java)
setRadiusMethod.invoke(rippleDrawable, value)
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
}

View File

@@ -1,30 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
class Constants {
companion object {
const val DEFAULT_COLOR = 0xFFBA68C8.toInt()
}
}

View File

@@ -1,490 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.util.StateSet
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
class DrawableBuilder {
private var properties = DrawableProperties()
private var order: AtomicInteger = AtomicInteger(1)
private var transformsMap = TreeMap<Int, (Drawable) -> Drawable>()
private var baseDrawable: Drawable? = null
fun batch(properties: DrawableProperties) = apply { this.properties = properties.copy() }
fun baseDrawable(drawable: Drawable) = apply { baseDrawable = drawable }
// <shape>
fun shape(shape: Int) = apply { properties.shape = shape }
fun rectangle() = apply { shape(GradientDrawable.RECTANGLE) }
fun oval() = apply { shape(GradientDrawable.OVAL) }
fun line() = apply { shape(GradientDrawable.LINE) }
fun ring() = apply { shape(GradientDrawable.RING) }
fun innerRadius(innerRadius: Int) = apply { properties.innerRadius = innerRadius }
fun innerRadiusRatio(innerRadiusRatio: Float) =
apply { properties.innerRadiusRatio = innerRadiusRatio }
fun thickness(thickness: Int) = apply { properties.thickness = thickness }
fun thicknessRatio(thicknessRatio: Float) = apply { properties.thicknessRatio = thicknessRatio }
fun useLevelForRing(use: Boolean = true) = apply { properties.useLevelForRing = use }
// <corner>
fun cornerRadius(cornerRadius: Int) = apply { properties.cornerRadius = cornerRadius }
fun topLeftRadius(topLeftRadius: Int) = apply { properties.topLeftRadius = topLeftRadius }
fun topRightRadius(topRightRadius: Int) = apply { properties.topRightRadius = topRightRadius }
fun bottomRightRadius(bottomRightRadius: Int) =
apply { properties.bottomRightRadius = bottomRightRadius }
fun bottomLeftRadius(bottomLeftRadius: Int) =
apply { properties.bottomLeftRadius = bottomLeftRadius }
fun rounded() = apply { cornerRadius(Int.MAX_VALUE) }
fun cornerRadii(
topLeftRadius: Int,
topRightRadius: Int,
bottomRightRadius: Int,
bottomLeftRadius: Int
) = apply {
topLeftRadius(topLeftRadius); topRightRadius(topRightRadius); bottomRightRadius(
bottomRightRadius
); bottomLeftRadius(bottomLeftRadius)
}
// <gradient>
fun gradient(useGradient: Boolean = true) = apply { properties.useGradient = useGradient }
fun gradientType(type: Int) = apply { properties.type = type }
fun linearGradient() = apply { gradientType(GradientDrawable.LINEAR_GRADIENT) }
fun radialGradient() = apply { gradientType(GradientDrawable.RADIAL_GRADIENT) }
fun sweepGradient() = apply { gradientType(GradientDrawable.SWEEP_GRADIENT) }
fun angle(angle: Int) = apply { properties.angle = angle }
fun centerX(centerX: Float) = apply { properties.centerX = centerX }
fun centerY(centerY: Float) = apply { properties.centerY = centerY }
fun center(centerX: Float, centerY: Float) = apply { centerX(centerX); centerY(centerY) }
fun useCenterColor(useCenterColor: Boolean = true) =
apply { properties.useCenterColor = useCenterColor }
fun startColor(startColor: Int) = apply { properties.startColor = startColor }
fun centerColor(centerColor: Int) = apply {
properties.centerColor = centerColor
useCenterColor(true)
}
fun endColor(endColor: Int) = apply { properties.endColor = endColor }
fun gradientColors(startColor: Int, endColor: Int, centerColor: Int?) = apply {
startColor(startColor); endColor(endColor)
useCenterColor(centerColor != null)
centerColor?.let {
centerColor(it)
}
}
fun gradientRadiusType(gradientRadiusType: Int) =
apply { properties.gradientRadiusType = gradientRadiusType }
fun gradientRadius(gradientRadius: Float) = apply { properties.gradientRadius = gradientRadius }
fun gradientRadius(radius: Float, type: Int) =
apply { gradientRadius(radius); gradientRadiusType(type) }
fun gradientRadiusInPixel(radius: Float) =
apply { gradientRadius(radius); gradientRadiusType(DrawableProperties.RADIUS_TYPE_PIXELS) }
fun gradientRadiusInFraction(radius: Float) =
apply { gradientRadius(radius); gradientRadiusType(DrawableProperties.RADIUS_TYPE_FRACTION) }
fun useLevelForGradient(use: Boolean) = apply { properties.useLevelForGradient = use }
fun useLevelForGradient() = apply { useLevelForGradient(true) }
// <size>
fun width(width: Int) = apply { properties.width = width }
fun height(height: Int) = apply { properties.height = height }
fun size(width: Int, height: Int) = apply { width(width); height(height) }
fun size(size: Int) = apply { width(size).height(size) }
// <solid>
fun solidColor(solidColor: Int) = apply { properties.solidColor = solidColor }
private var solidColorPressed: Int? = null
fun solidColorPressed(color: Int?) = apply { solidColorPressed = color }
private var solidColorPressedWhenRippleUnsupported: Int? = null
fun solidColorPressedWhenRippleUnsupported(color: Int?) =
apply { solidColorPressedWhenRippleUnsupported = color }
private var solidColorDisabled: Int? = null
fun solidColorDisabled(color: Int?) = apply { solidColorDisabled = color }
private var solidColorSelected: Int? = null
fun solidColorSelected(color: Int?) = apply { solidColorSelected = color }
fun solidColorStateList(colorStateList: ColorStateList) =
apply { properties.solidColorStateList = colorStateList }
// <stroke>
fun strokeWidth(strokeWidth: Int) = apply { properties.strokeWidth = strokeWidth }
fun strokeColor(strokeColor: Int) = apply { properties.strokeColor = strokeColor }
private var strokeColorPressed: Int? = null
fun strokeColorPressed(color: Int?) = apply { strokeColorPressed = color }
private var strokeColorDisabled: Int? = null
fun strokeColorDisabled(color: Int?) = apply { strokeColorDisabled = color }
private var strokeColorSelected: Int? = null
fun strokeColorSelected(color: Int?) = apply { strokeColorSelected = color }
fun strokeColorStateList(colorStateList: ColorStateList) =
apply { properties.strokeColorStateList = colorStateList }
fun dashWidth(dashWidth: Int) = apply { properties.dashWidth = dashWidth }
fun dashGap(dashGap: Int) = apply { properties.dashGap = dashGap }
fun hairlineBordered() = apply { strokeWidth(1) }
fun shortDashed() = apply { dashWidth(12).dashGap(12) }
fun mediumDashed() = apply { dashWidth(24).dashGap(24) }
fun longDashed() = apply { dashWidth(36).dashGap(36) }
fun dashed() = apply { mediumDashed() }
// <rotate>
private var rotateOrder = 0
fun rotate(boolean: Boolean = true) = apply {
properties.useRotate = boolean
rotateOrder = if (boolean) {
order.getAndIncrement()
} else {
0
}
}
fun pivotX(pivotX: Float) = apply { properties.pivotX = pivotX }
fun pivotY(pivotY: Float) = apply { properties.pivotY = pivotY }
fun pivot(pivotX: Float, pivotY: Float) = apply { pivotX(pivotX).pivotY(pivotY) }
fun fromDegrees(degrees: Float) = apply { properties.fromDegrees = degrees }
fun toDegrees(degrees: Float) = apply { properties.toDegrees = degrees }
fun degrees(fromDegrees: Float, toDegrees: Float) =
apply { fromDegrees(fromDegrees).toDegrees(toDegrees) }
fun degrees(degrees: Float) = apply { fromDegrees(degrees).toDegrees(degrees) }
fun rotate(fromDegrees: Float, toDegrees: Float) =
apply { rotate().fromDegrees(fromDegrees).toDegrees(toDegrees) }
fun rotate(degrees: Float) = apply { rotate().degrees(degrees) }
// <scale>
private var scaleOrder = 0
fun scale(boolean: Boolean = true) = apply {
properties.useScale = boolean
scaleOrder = if (boolean) {
order.getAndIncrement()
} else {
0
}
}
fun scaleLevel(level: Int) = apply { properties.scaleLevel = level }
fun scaleGravity(gravity: Int) = apply { properties.scaleGravity = gravity }
fun scaleWidth(scale: Float) = apply { properties.scaleWidth = scale }
fun scaleHeight(scale: Float) = apply { properties.scaleHeight = scale }
fun scale(scale: Float) = apply { scale().scaleWidth(scale).scaleHeight(scale) }
fun scale(scaleWidth: Float, scaleHeight: Float) =
apply { scale().scaleWidth(scaleWidth).scaleHeight(scaleHeight) }
// flip
fun flip(boolean: Boolean = true) = apply { properties.useFlip = boolean }
fun orientation(orientation: Int) = apply { properties.orientation = orientation }
fun flipVertical() = apply { flip().orientation(FlipDrawable.ORIENTATION_VERTICAL) }
// <ripple>
fun ripple(boolean: Boolean = true) = apply { properties.useRipple = boolean }
fun rippleColor(color: Int) = apply { properties.rippleColor = color }
fun rippleColorStateList(colorStateList: ColorStateList) =
apply { properties.rippleColorStateList = colorStateList }
fun rippleRadius(radius: Int) = apply { properties.rippleRadius = radius }
fun build(): Drawable {
if (baseDrawable != null) {
return wrap(baseDrawable!!)
}
var drawable: Drawable
// fall back when ripple is unavailable on devices with API < 21
if (shouldFallbackRipple()) {
if (solidColorPressedWhenRippleUnsupported != null) {
solidColorPressed(solidColorPressedWhenRippleUnsupported)
} else {
solidColorPressed(properties.rippleColor)
}
}
if (needStateListDrawable()) {
drawable = StateListDrawableBuilder()
.pressed(buildPressedDrawable())
.disabled(buildDisabledDrawable())
.selected(buildSelectedDrawable())
.normal(buildNormalDrawable())
.build()
} else {
drawable = GradientDrawable()
setupGradientDrawable(drawable)
}
drawable = wrap(drawable)
return drawable
}
private fun getSolidColorStateList(): ColorStateList {
if (properties.solidColorStateList != null) {
return properties.solidColorStateList!!
}
val states = mutableListOf<IntArray>()
val colors = mutableListOf<Int>()
solidColorPressed?.let {
states.add(intArrayOf(android.R.attr.state_pressed))
colors.add(it)
}
solidColorDisabled?.let {
states.add(intArrayOf(-android.R.attr.state_enabled))
colors.add(it)
}
solidColorSelected?.let {
states.add(intArrayOf(android.R.attr.state_selected))
colors.add(it)
}
states.add(StateSet.WILD_CARD)
colors.add(properties.solidColor)
return ColorStateList(states.toTypedArray(), colors.toIntArray())
}
private fun getStrokeColorStateList(): ColorStateList {
if (properties.strokeColorStateList != null) {
return properties.strokeColorStateList!!
}
val states = mutableListOf<IntArray>()
val colors = mutableListOf<Int>()
strokeColorPressed?.let {
states.add(intArrayOf(android.R.attr.state_pressed))
colors.add(it)
}
strokeColorDisabled?.let {
states.add(intArrayOf(-android.R.attr.state_enabled))
colors.add(it)
}
strokeColorSelected?.let {
states.add(intArrayOf(android.R.attr.state_selected))
colors.add(it)
}
states.add(StateSet.WILD_CARD)
colors.add(properties.strokeColor)
return ColorStateList(states.toTypedArray(), colors.toIntArray())
}
private fun buildPressedDrawable(): Drawable? {
if (solidColorPressed == null && strokeColorPressed == null) return null
val pressedDrawable = GradientDrawable()
setupGradientDrawable(pressedDrawable)
solidColorPressed?.let {
pressedDrawable.setColor(it)
}
strokeColorPressed?.let {
setStrokeColor(pressedDrawable, it)
}
return pressedDrawable
}
private fun buildDisabledDrawable(): Drawable? {
if (solidColorDisabled == null && strokeColorDisabled == null) return null
val disabledDrawable = GradientDrawable()
setupGradientDrawable(disabledDrawable)
solidColorDisabled?.let {
disabledDrawable.setColor(it)
}
strokeColorDisabled?.let {
setStrokeColor(disabledDrawable, it)
}
return disabledDrawable
}
private fun buildSelectedDrawable(): Drawable? {
if (solidColorSelected == null && strokeColorSelected == null) return null
val selectedDrawable = GradientDrawable()
setupGradientDrawable(selectedDrawable)
solidColorSelected?.let {
selectedDrawable.setColor(it)
}
strokeColorSelected?.let {
setStrokeColor(selectedDrawable, it)
}
return selectedDrawable
}
private fun buildNormalDrawable(): Drawable {
val pressedDrawable = GradientDrawable()
setupGradientDrawable(pressedDrawable)
return pressedDrawable
}
private fun setupGradientDrawable(drawable: GradientDrawable) {
properties.apply {
drawable.shape = shape
if (shape == GradientDrawable.RING) {
setInnerRadius(drawable, innerRadius)
setInnerRadiusRatio(drawable, innerRadiusRatio)
setThickness(drawable, thickness)
setThicknessRatio(drawable, thicknessRatio)
setUseLevelForShape(drawable, useLevelForRing)
}
drawable.cornerRadii = getCornerRadii()
if (useGradient) {
drawable.gradientType = type
setGradientRadiusType(drawable, gradientRadiusType)
setGradientRadius(drawable, gradientRadius)
drawable.setGradientCenter(centerX, centerY)
setOrientation(drawable, getOrientation())
setColors(drawable, getColors())
drawable.useLevel = useLevelForGradient
} else {
drawable.color = getSolidColorStateList()
}
drawable.setSize(width, height)
drawable.setStroke(
strokeWidth,
getStrokeColorStateList(),
dashWidth.toFloat(),
dashGap.toFloat()
)
}
}
private fun needStateListDrawable(): Boolean {
return (hasStrokeColorStateList() || (!properties.useGradient && hasSolidColorStateList()))
}
private fun needRotateDrawable(): Boolean {
return properties.useRotate &&
!(properties.pivotX == 0.5f && properties.pivotY == 0.5f
&& properties.fromDegrees == 0f && properties.toDegrees == 0f)
}
private fun needScaleDrawable(): Boolean {
return properties.useScale
}
private fun wrap(drawable: Drawable): Drawable {
var wrappedDrawable = drawable
if (rotateOrder > 0) {
transformsMap[rotateOrder] = ::wrapRotateIfNeeded
}
if (scaleOrder > 0) {
transformsMap[scaleOrder] = ::wrapScaleIfNeeded
}
for (action in transformsMap.values) {
wrappedDrawable = action.invoke(wrappedDrawable)
}
if (properties.useFlip) {
wrappedDrawable = FlipDrawableBuilder()
.drawable(wrappedDrawable)
.orientation(properties.orientation)
.build()
}
if (isRippleSupported() && properties.useRipple) {
wrappedDrawable = RippleDrawableBuilder()
.drawable(wrappedDrawable)
.color(properties.rippleColor)
.colorStateList(properties.rippleColorStateList)
.radius(properties.rippleRadius)
.build()
}
return wrappedDrawable
}
private fun shouldFallbackRipple(): Boolean {
return properties.useRipple && !isRippleSupported()
}
private fun isRippleSupported() = true
private fun wrapRotateIfNeeded(drawable: Drawable): Drawable {
if (!needRotateDrawable()) return drawable
with(properties) {
return RotateDrawableBuilder()
.drawable(drawable)
.pivotX(pivotX)
.pivotY(pivotY)
.fromDegrees(fromDegrees)
.toDegrees(toDegrees)
.build()
}
}
private fun wrapScaleIfNeeded(drawable: Drawable): Drawable {
if (!needScaleDrawable()) return drawable
with(properties) {
return ScaleDrawableBuilder()
.drawable(drawable)
.level(scaleLevel)
.scaleGravity(scaleGravity)
.scaleWidth(scaleWidth)
.scaleHeight(scaleHeight)
.build()
}
}
private fun hasSolidColorStateList(): Boolean {
return solidColorPressed != null || solidColorDisabled != null || solidColorSelected != null
}
private fun hasStrokeColorStateList(): Boolean {
return strokeColorPressed != null || strokeColorDisabled != null || strokeColorSelected != null
}
}

View File

@@ -1,222 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
@file:Suppress("SetterBackingFieldAssignment", "unused")
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Parcel
import android.os.Parcelable
import android.view.Gravity
import java.io.Serializable
data class DrawableProperties(
// <shape>
@JvmField var shape: Int = GradientDrawable.RECTANGLE,
@JvmField var innerRadius: Int = -1,
@JvmField var innerRadiusRatio: Float = 9f,
@JvmField var thickness: Int = -1,
@JvmField var thicknessRatio: Float = 3f,
@JvmField var useLevelForRing: Boolean = false,
// <corner>
private var _cornerRadius: Int = 0,
@JvmField var topLeftRadius: Int = 0,
@JvmField var topRightRadius: Int = 0,
@JvmField var bottomRightRadius: Int = 0,
@JvmField var bottomLeftRadius: Int = 0,
// <gradient>
@JvmField var useGradient: Boolean = false,
@JvmField var type: Int = GradientDrawable.RADIAL_GRADIENT,
@JvmField var angle: Int = 0,
@JvmField var centerX: Float = 0.5f,
@JvmField var centerY: Float = 0.5f,
@JvmField var useCenterColor: Boolean = false,
@JvmField var startColor: Int = Constants.DEFAULT_COLOR,
@JvmField var centerColor: Int? = null,
@JvmField var endColor: Int = 0x7FFFFFFF,
@JvmField var gradientRadiusType: Int = RADIUS_TYPE_FRACTION,
@JvmField var gradientRadius: Float = 0.5f,
@JvmField var useLevelForGradient: Boolean = false,
// <size>
@JvmField var width: Int = -1,
@JvmField var height: Int = -1,
// <solid>
@JvmField var solidColor: Int = Color.TRANSPARENT,
@JvmField var solidColorStateList: ColorStateList? = null,
// <stroke>
@JvmField var strokeWidth: Int = 0,
@JvmField var strokeColor: Int = Color.DKGRAY,
@JvmField var strokeColorStateList: ColorStateList? = null,
@JvmField var dashWidth: Int = 0,
@JvmField var dashGap: Int = 0,
// <rotate>
@JvmField var useRotate: Boolean = false,
@JvmField var pivotX: Float = 0.5f,
@JvmField var pivotY: Float = 0.5f,
@JvmField var fromDegrees: Float = 0f,
@JvmField var toDegrees: Float = 0f,
// <scale>
@JvmField var useScale: Boolean = false,
@JvmField var scaleLevel: Int = 10000,
@JvmField var scaleGravity: Int = Gravity.CENTER,
@JvmField var scaleWidth: Float = 0f,
@JvmField var scaleHeight: Float = 0f,
// flip
@JvmField var useFlip: Boolean = false,
@JvmField var orientation: Int = FlipDrawable.ORIENTATION_HORIZONTAL,
// ripple
@JvmField var useRipple: Boolean = false,
@JvmField var rippleColor: Int = Constants.DEFAULT_COLOR,
@JvmField var rippleColorStateList: ColorStateList? = null,
@JvmField var rippleRadius: Int = -1
) : Serializable {
companion object {
const val RADIUS_TYPE_PIXELS = 0
const val RADIUS_TYPE_FRACTION = 1
@JvmField
val CREATOR = object : Parcelable.Creator<DrawableProperties> {
override fun createFromParcel(parcel: Parcel): DrawableProperties {
return DrawableProperties(parcel)
}
override fun newArray(size: Int): Array<DrawableProperties?> {
return arrayOfNulls(size)
}
}
}
var cornerRadius: Int = _cornerRadius
set(value) {
_cornerRadius = value
topLeftRadius = value
topRightRadius = value
bottomRightRadius = value
bottomLeftRadius = value
}
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readInt(),
parcel.readFloat(),
parcel.readInt(),
parcel.readFloat(),
parcel.readByte() != 0.toByte(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt(),
parcel.readByte() != 0.toByte(),
parcel.readInt(),
parcel.readInt(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readByte() != 0.toByte(),
parcel.readInt(),
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readInt(),
parcel.readInt(),
parcel.readFloat(),
parcel.readByte() != 0.toByte(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt(),
parcel.readParcelable(ColorStateList::class.java.classLoader),
parcel.readInt(),
parcel.readInt(),
parcel.readParcelable(ColorStateList::class.java.classLoader),
parcel.readInt(),
parcel.readInt(),
parcel.readByte() != 0.toByte(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readByte() != 0.toByte(),
parcel.readInt(),
parcel.readInt(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readByte() != 0.toByte(),
parcel.readInt(),
parcel.readByte() != 0.toByte(),
parcel.readInt(),
parcel.readParcelable(ColorStateList::class.java.classLoader),
parcel.readInt()
)
fun copy(): DrawableProperties {
val parcel = Parcel.obtain()
parcel.setDataPosition(0)
val properties = CREATOR.createFromParcel(parcel)
parcel.recycle()
return properties
}
fun getCornerRadii(): FloatArray {
return floatArrayOf(
topLeftRadius.toFloat(), topLeftRadius.toFloat(),
topRightRadius.toFloat(), topRightRadius.toFloat(),
bottomRightRadius.toFloat(), bottomRightRadius.toFloat(),
bottomLeftRadius.toFloat(), bottomLeftRadius.toFloat()
)
}
fun getOrientation(): GradientDrawable.Orientation {
val orientation: GradientDrawable.Orientation = when (val angle = this.angle % 360) {
0 -> GradientDrawable.Orientation.LEFT_RIGHT
45 -> GradientDrawable.Orientation.BL_TR
90 -> GradientDrawable.Orientation.BOTTOM_TOP
135 -> GradientDrawable.Orientation.BR_TL
180 -> GradientDrawable.Orientation.RIGHT_LEFT
225 -> GradientDrawable.Orientation.TR_BL
270 -> GradientDrawable.Orientation.TOP_BOTTOM
315 -> GradientDrawable.Orientation.TL_BR
else -> throw IllegalArgumentException("Unsupported angle: $angle")
}
return orientation
}
fun getColors(): IntArray {
return if (useCenterColor && centerColor != null) {
intArrayOf(startColor, centerColor!!, endColor)
} else intArrayOf(startColor, endColor)
}
fun materialization(): Drawable = DrawableBuilder().batch(this).build()
}

View File

@@ -1,35 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.drawable.Drawable
abstract class DrawableWrapperBuilder<T : DrawableWrapperBuilder<T>> {
protected var drawable: Drawable? = null
@Suppress("UNCHECKED_CAST")
fun drawable(drawable: Drawable): T = apply { this.drawable = drawable } as T
abstract fun build(): Drawable
}

View File

@@ -1,79 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
@file:Suppress("DEPRECATION", "CanvasSize")
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Rect
import android.graphics.drawable.Drawable
class FlipDrawable(
private var drawable: Drawable,
private var orientation: Int = ORIENTATION_HORIZONTAL
) : Drawable() {
companion object {
const val ORIENTATION_HORIZONTAL = 0
const val ORIENTATION_VERTICAL = 1
}
override fun draw(canvas: Canvas) {
val saveCount = canvas.save()
if (orientation == ORIENTATION_VERTICAL) {
canvas.scale(1f, -1f, (canvas.width / 2).toFloat(), (canvas.height / 2).toFloat())
} else {
canvas.scale(-1f, 1f, (canvas.width / 2).toFloat(), (canvas.height / 2).toFloat())
}
drawable.bounds = Rect(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
canvas.restoreToCount(saveCount)
}
override fun onLevelChange(level: Int): Boolean {
drawable.level = level
invalidateSelf()
return true
}
override fun getIntrinsicWidth(): Int {
return drawable.intrinsicWidth
}
override fun getIntrinsicHeight(): Int {
return drawable.intrinsicHeight
}
override fun setAlpha(alpha: Int) {
drawable.alpha = alpha
}
override fun getOpacity(): Int {
return drawable.opacity
}
override fun setColorFilter(colorFilter: ColorFilter?) {
drawable.colorFilter = colorFilter
}
}

View File

@@ -1,36 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.drawable.Drawable
class FlipDrawableBuilder : DrawableWrapperBuilder<FlipDrawableBuilder>() {
private var orientation: Int = FlipDrawable.ORIENTATION_HORIZONTAL
fun orientation(orientation: Int) = apply { this.orientation = orientation }
override fun build(): Drawable {
return FlipDrawable(drawable!!, orientation)
}
}

View File

@@ -1,182 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.os.Build
import android.view.Gravity
import androidx.annotation.RequiresApi
class LayerDrawableBuilder {
companion object {
const val DIMEN_UNDEFINED = Int.MIN_VALUE
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private var paddingMode = LayerDrawable.PADDING_MODE_NEST
private var paddingLeft = 0
private var paddingTop = 0
private var paddingRight = 0
private var paddingBottom = 0
private var paddingStart = 0
private var paddingEnd = 0
private val layers = ArrayList<Layer>()
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun paddingMode(mode: Int) = apply { paddingMode = mode }
fun paddingLeft(padding: Int) = apply { paddingLeft = padding }
fun paddingTop(padding: Int) = apply { paddingTop = padding }
fun paddingRight(padding: Int) = apply { paddingRight = padding }
fun paddingBottom(padding: Int) = apply { paddingBottom = padding }
@RequiresApi(Build.VERSION_CODES.M)
fun paddingStart(padding: Int) = apply { paddingStart = padding }
@RequiresApi(Build.VERSION_CODES.M)
fun paddingEnd(padding: Int) = apply { paddingEnd = padding }
fun padding(padding: Int) = apply {
paddingLeft(padding).paddingTop(padding).paddingRight(padding).paddingBottom(padding)
}
@RequiresApi(Build.VERSION_CODES.M)
fun paddingRelative(padding: Int) = apply {
paddingStart(padding).paddingTop(padding).paddingEnd(padding).paddingBottom(padding)
}
fun add(drawable: Drawable) = apply { layers.add(Layer(drawable)) }
fun width(width: Int) = apply { layers.last().width = width }
fun height(height: Int) = apply { layers.last().height = height }
fun insetLeft(inset: Int) = apply { layers.last().insetLeft = inset }
fun insetTop(inset: Int) = apply { layers.last().insetTop = inset }
fun insetRight(inset: Int) = apply { layers.last().insetRight = inset }
fun insetBottom(inset: Int) = apply { layers.last().insetBottom = inset }
@RequiresApi(Build.VERSION_CODES.M)
fun insetStart(inset: Int) = apply { layers.last().insetStart = inset }
@RequiresApi(Build.VERSION_CODES.M)
fun insetEnd(inset: Int) = apply { layers.last().insetEnd = inset }
fun inset(inset: Int) =
apply { insetLeft(inset).insetTop(inset).insetRight(inset).insetBottom(inset) }
fun inset(insetLeft: Int, insetTop: Int, insetRight: Int, insetBottom: Int) = apply {
insetLeft(insetLeft).insetTop(insetTop).insetRight(insetRight).insetBottom(insetBottom)
}
@RequiresApi(Build.VERSION_CODES.M)
fun insetRelative(inset: Int) =
apply { insetStart(inset).insetTop(inset).insetEnd(inset).insetBottom(inset) }
@RequiresApi(Build.VERSION_CODES.M)
fun insetRelative(insetStart: Int, insetTop: Int, insetEnd: Int, insetBottom: Int) = apply {
insetStart(insetStart).insetTop(insetTop).insetEnd(insetEnd).insetBottom(insetBottom)
}
@RequiresApi(Build.VERSION_CODES.M)
fun gravity(gravity: Int) = apply { layers.last().gravity = gravity }
fun modify(
index: Int, drawable: Drawable,
width: Int = DIMEN_UNDEFINED,
height: Int = DIMEN_UNDEFINED,
insetLeft: Int = DIMEN_UNDEFINED,
insetTop: Int = DIMEN_UNDEFINED,
insetRight: Int = DIMEN_UNDEFINED,
insetBottom: Int = DIMEN_UNDEFINED,
insetStart: Int = DIMEN_UNDEFINED,
insetEnd: Int = DIMEN_UNDEFINED
) =
apply {
val layer = layers[index]
layer.drawable = drawable
if (width != DIMEN_UNDEFINED) layer.width = width
if (height != DIMEN_UNDEFINED) layer.height = height
if (insetLeft != DIMEN_UNDEFINED) layer.insetLeft = insetLeft
if (insetTop != DIMEN_UNDEFINED) layer.insetTop = insetTop
if (insetRight != DIMEN_UNDEFINED) layer.insetRight = insetRight
if (insetBottom != DIMEN_UNDEFINED) layer.insetBottom = insetBottom
if (insetStart != DIMEN_UNDEFINED) layer.insetStart = insetStart
if (insetEnd != DIMEN_UNDEFINED) layer.insetEnd = insetEnd
}
fun build(): LayerDrawable {
val layerDrawable = LayerDrawable(layers.map { it -> it.drawable }.toTypedArray())
for (i in 0 until layers.size) {
val layer = layers[i]
layerDrawable.setLayerInset(
i,
layer.insetLeft,
layer.insetTop,
layer.insetRight,
layer.insetBottom
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (layer.insetStart != DIMEN_UNDEFINED || layer.insetEnd != DIMEN_UNDEFINED) {
layerDrawable.setLayerInsetRelative(
i,
layer.insetStart,
layer.insetTop,
layer.insetEnd,
layer.insetBottom
)
}
}
layerDrawable.setId(i, i)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
layerDrawable.setLayerGravity(i, layer.gravity)
layerDrawable.setLayerInsetStart(i, layer.insetStart)
layerDrawable.setLayerInsetEnd(i, layer.insetEnd)
}
}
layerDrawable.paddingMode = paddingMode
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
layerDrawable.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
if (paddingStart != DIMEN_UNDEFINED || paddingEnd != DIMEN_UNDEFINED) {
layerDrawable.setPaddingRelative(
paddingStart,
paddingTop,
paddingEnd,
paddingBottom
)
}
}
return layerDrawable
}
internal class Layer(var drawable: Drawable) {
var gravity: Int = Gravity.NO_GRAVITY
var width: Int = -1
var height: Int = -1
var insetLeft: Int = 0
var insetTop: Int = 0
var insetRight: Int = 0
var insetBottom: Int = 0
var insetStart: Int = DIMEN_UNDEFINED
var insetEnd: Int = DIMEN_UNDEFINED
}
}

View File

@@ -1,62 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.Path
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.PathShape
class PathShapeDrawableBuilder {
private var path: Path? = null
private var pathStandardWidth: Float = 100f
private var pathStandardHeight: Float = 100f
private var width: Int = -1
private var height: Int = -1
fun path(path: Path, pathStandardWidth: Float, pathStandardHeight: Float) = apply {
this.path = path
this.pathStandardWidth = pathStandardWidth
this.pathStandardHeight = pathStandardHeight
}
fun width(width: Int) = apply { this.width = width }
fun height(height: Int) = apply { this.height = height }
fun size(size: Int) = apply { width(size).height(size) }
fun build(custom: ((shapeDrawable: ShapeDrawable) -> Unit)? = null): ShapeDrawable {
val shapeDrawable = ShapeDrawable()
if (path == null || width <= 0 || height <= 0) {
return shapeDrawable
}
val pathShape = PathShape(path!!, pathStandardWidth, pathStandardHeight)
shapeDrawable.shape = pathShape
shapeDrawable.intrinsicWidth = width
shapeDrawable.intrinsicHeight = height
if (custom != null) {
custom(shapeDrawable)
}
return shapeDrawable
}
}

View File

@@ -1,74 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.*
import android.util.StateSet
class RippleDrawableBuilder : DrawableWrapperBuilder<RippleDrawableBuilder>() {
private var color: Int = Constants.DEFAULT_COLOR
private var colorStateList: ColorStateList? = null
private var radius: Int = -1
fun color(color: Int) = apply { this.color = color }
fun colorStateList(colorStateList: ColorStateList?) =
apply { this.colorStateList = colorStateList }
fun radius(radius: Int) = apply { this.radius = radius }
override fun build(): Drawable {
var drawable = this.drawable!!
val colorStateList = this.colorStateList ?: ColorStateList(
arrayOf(StateSet.WILD_CARD),
intArrayOf(color)
)
var mask = if (drawable is DrawableContainer) drawable.getCurrent() else drawable
if (mask is ShapeDrawable) {
val state = mask.getConstantState()
if (state != null) {
val temp = state.newDrawable().mutate() as ShapeDrawable
temp.paint.color = Color.BLACK
mask = temp
}
} else if (mask is GradientDrawable) {
val state = mask.getConstantState()
if (state != null) {
val temp = state.newDrawable().mutate() as GradientDrawable
temp.setColor(Color.BLACK)
mask = temp
}
} else {
mask = ColorDrawable(Color.BLACK)
}
val rippleDrawable = RippleDrawable(colorStateList, drawable, mask)
setRadius(rippleDrawable, radius)
rippleDrawable.invalidateSelf()
drawable = rippleDrawable
return drawable
}
}

View File

@@ -1,53 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.drawable.Drawable
import android.graphics.drawable.RotateDrawable
class RotateDrawableBuilder : DrawableWrapperBuilder<RotateDrawableBuilder>() {
private var pivotX: Float = 0.5f
private var pivotY: Float = 0.5f
private var fromDegrees: Float = 0f
private var toDegrees: Float = 360f
fun pivotX(x: Float) = apply { pivotX = x }
fun pivotY(y: Float) = apply { pivotY = y }
fun fromDegrees(degree: Float) = apply { fromDegrees = degree }
fun toDegrees(degree: Float) = apply { toDegrees = degree }
override fun build(): Drawable {
val rotateDrawable = RotateDrawable()
drawable?.let {
setDrawable(rotateDrawable, it)
apply {
setPivotX(rotateDrawable, pivotX)
setPivotY(rotateDrawable, pivotY)
setFromDegrees(rotateDrawable, fromDegrees)
setToDegrees(rotateDrawable, toDegrees)
}
}
return rotateDrawable
}
}

View File

@@ -1,46 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.drawable.Drawable
import android.graphics.drawable.ScaleDrawable
import android.view.Gravity
class ScaleDrawableBuilder : DrawableWrapperBuilder<ScaleDrawableBuilder>() {
private var level: Int = 10000
private var scaleGravity = Gravity.CENTER
private var scaleWidth: Float = 0f
private var scaleHeight: Float = 0f
fun level(level: Int) = apply { this.level = level }
fun scaleGravity(gravity: Int) = apply { this.scaleGravity = gravity }
fun scaleWidth(scale: Float) = apply { this.scaleWidth = scale }
fun scaleHeight(scale: Float) = apply { this.scaleHeight = scale }
override fun build(): Drawable {
val scaleDrawable = ScaleDrawable(drawable, scaleGravity, scaleWidth, scaleHeight)
scaleDrawable.level = level
return scaleDrawable
}
}

View File

@@ -1,61 +0,0 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/8.
*/
package com.fankes.coloros.notify.utils.drawable.drawabletoolbox
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.StateListDrawable
import android.util.StateSet
class StateListDrawableBuilder {
private var pressed: Drawable? = null
private var disabled: Drawable? = null
private var selected: Drawable? = null
private var normal: Drawable = ColorDrawable(Color.TRANSPARENT)
fun pressed(pressed: Drawable?) = apply { this.pressed = pressed }
fun disabled(disabled: Drawable?) = apply { this.disabled = disabled }
fun selected(selected: Drawable?) = apply { this.selected = selected }
fun normal(normal: Drawable) = apply { this.normal = normal }
fun build(): StateListDrawable {
val stateListDrawable = StateListDrawable()
setupStateListDrawable(stateListDrawable)
return stateListDrawable
}
private fun setupStateListDrawable(stateListDrawable: StateListDrawable) {
pressed?.let {
stateListDrawable.addState(intArrayOf(android.R.attr.state_pressed), it)
}
disabled?.let {
stateListDrawable.addState(intArrayOf(-android.R.attr.state_enabled), it)
}
selected?.let {
stateListDrawable.addState(intArrayOf(android.R.attr.state_selected), it)
}
stateListDrawable.addState(StateSet.WILD_CARD, normal)
}
}

View File

@@ -0,0 +1,66 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/10/6.
*/
package com.fankes.coloros.notify.utils.factory
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
/** 已添加的返回监听事件 */
private val onBackPressedCallbacks = HashMap<AppCompatActivity, OnBackPressedCallback>()
/**
* 手动调用返回事件
* @param ignored 是否忽略现有返回监听事件立即返回 - 否则将执行返回事件 - 默认否
*/
fun AppCompatActivity.callOnBackPressed(ignored: Boolean = false) {
if (isDestroyed) return
onBackPressedCallbacks[this]?.isEnabled = ignored.not()
onBackPressedDispatcher.onBackPressed()
}
/**
* 添加返回监听事件
* @param callback 回调事件
*/
fun AppCompatActivity.addOnBackPressedEvent(callback: OnBackPressedEvent.() -> Unit) {
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
OnBackPressedEvent(this@addOnBackPressedEvent).apply(callback)
}
}.also { result ->
onBackPressedCallbacks.computeIfAbsent(this) {
onBackPressedDispatcher.addCallback(result)
result
}
}
}
/**
* 返回监听事件实现类
* @param instance 当前实例
*/
class OnBackPressedEvent(private val instance: AppCompatActivity) {
/** 立即释放返回事件并调用返回功能 */
fun releaseEventAndBack() = instance.callOnBackPressed(ignored = true)
}

View File

@@ -0,0 +1,89 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/6/3.
*/
@file:Suppress("DEPRECATION")
package com.fankes.coloros.notify.utils.factory
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ListView
import androidx.viewbinding.ViewBinding
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
/**
* 绑定 [BaseAdapter] 到 [ListView]
* @param initiate 方法体
* @return [BaseAdapter]
*/
inline fun ListView.bindAdapter(initiate: BaseAdapterCreater.() -> Unit) =
BaseAdapterCreater(context).apply(initiate).baseAdapter?.apply { adapter = this } ?: error("BaseAdapter not binded")
/**
* [BaseAdapter] 创建类
* @param context 实例
*/
class BaseAdapterCreater(val context: Context) {
/** 当前 [List] 回调 */
var listDataCallback: (() -> List<*>)? = null
/** 当前 [BaseAdapter] */
var baseAdapter: BaseAdapter? = null
/**
* 绑定 [List] 到 [ListView]
* @param result 回调数据
*/
fun onBindDatas(result: (() -> List<*>)) {
listDataCallback = result
}
/**
* 绑定 [BaseAdapter] 到 [ListView]
* @param bindViews 回调 - ([VB] 每项,[Int] 下标)
*/
inline fun <reified VB : ViewBinding> onBindViews(crossinline bindViews: (binding: VB, position: Int) -> Unit) {
baseAdapter = object : BaseAdapter() {
override fun getCount() = listDataCallback?.let { it() }?.size ?: 0
override fun getItem(position: Int) = listDataCallback?.let { it() }?.get(position)
override fun getItemId(position: Int) = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var holderView = convertView
val holder: VB
if (convertView == null) {
holder = VB::class.java.method {
name = "inflate"
param(LayoutInflaterClass)
}.get().invoke<VB>(LayoutInflater.from(context)) ?: error("ViewHolder binding failed")
holderView = holder.root.apply { tag = holder }
} else holder = convertView.tag as VB
bindViews(holder, position)
return holderView ?: error("ViewHolder binding failed")
}
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,102 +18,105 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/7.
* This file is created by fankes on 2022/1/7.
*/
@file:Suppress("unused", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE")
@file:Suppress("unused", "DEPRECATION")
package com.fankes.coloros.notify.utils.factory
import android.app.Dialog
import android.app.TimePickerDialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AlertDialog
import androidx.viewbinding.ViewBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.highcapable.yukihookapi.annotation.CauseProblemsApi
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
/**
* 构造对话框
* @param isUseBlackTheme 是否使用深色主题
* @param it 对话框方法体
*/
fun Context.showDialog(isUseBlackTheme: Boolean = false, it: DialogBuilder.() -> Unit) =
DialogBuilder(this, isUseBlackTheme).apply(it).show()
/**
* 显示时间选择对话框
* @param timeSet 当前时间 - 不写将使用当前时间格式HH:mm
* @param it 回调 - 小时与分钟 HH:mm
* @param result 回调 - 小时与分钟 HH:mm
*/
fun Context.showTimePicker(timeSet: String = "", it: (String) -> Unit) =
TimePickerDialog(this, { _, h, m -> it("${h.autoZero}:${m.autoZero}") }, timeSet.hour, timeSet.minute, true).show()
fun Context.showTimePicker(timeSet: String = "", result: (String) -> Unit) =
TimePickerDialog(this, { _, h, m -> result("${h.autoZero}:${m.autoZero}") }, timeSet.hour, timeSet.minute, true).show()
/**
* 构造 [VB] 自定义 View 对话框
* @param initiate 对话框方法体
*/
@JvmName(name = "showDialog_Generics")
inline fun <reified VB : ViewBinding> Context.showDialog(initiate: DialogBuilder<VB>.() -> Unit) =
DialogBuilder<VB>(context = this, VB::class.java).apply(initiate).show()
/**
* 构造对话框
* @param initiate 对话框方法体
*/
inline fun Context.showDialog(initiate: DialogBuilder<*>.() -> Unit) = DialogBuilder<ViewBinding>(context = this).apply(initiate).show()
/**
* 对话框构造器
* @param context 实例
* @param isUseBlackTheme 是否使用深色主题 - 对 AndroidX 风格无效
* @param bindingClass [ViewBinding] 的 [Class] 实例 or null
*/
class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean) {
class DialogBuilder<VB : ViewBinding>(val context: Context, private val bindingClass: Class<*>? = null) {
private var instanceAndroidX: androidx.appcompat.app.AlertDialog.Builder? = null // 实例对象
private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象
/** 实例对象 */
private var instance: AlertDialog.Builder? = null
private var dialogInstance: Dialog? = null // 对话框实例
/** 对话框取消监听 */
private var onCancel: (() -> Unit)? = null
@CauseProblemsApi
var customLayoutView: View? = null // 自定义布局
/** 对话框实例 */
private var dialogInstance: Dialog? = null
/** 自定义布局 */
private var customLayoutView: View? = null
/**
* 是否需要使用 AndroidX 风格对话框
* @return [Boolean]
* 获取 [DialogBuilder] 绑定布局对象
* @return [VB]
*/
private val isUsingAndroidX get() = runCatching { context is AppCompatActivity }.getOrNull() ?: false
val binding by lazy {
bindingClass?.method {
name = "inflate"
param(LayoutInflaterClass)
}?.get()?.invoke<VB>(LayoutInflater.from(context))?.apply {
customLayoutView = root
} ?: error("This dialog maybe not a custom view dialog")
}
init {
if (isUsingAndroidX)
runInSafe { instanceAndroidX = MaterialAlertDialogBuilder(context) }
else runInSafe {
instanceAndroid = android.app.AlertDialog.Builder(
context,
if (isUseBlackTheme) android.R.style.Theme_Material_Dialog else android.R.style.Theme_Material_Light_Dialog
)
}
if (YukiHookAPI.Status.isXposedEnvironment) error("This dialog is not allowed to created in Xposed environment")
instance = MaterialAlertDialogBuilder(context)
}
/** 设置对话框不可关闭 */
fun noCancelable() {
if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setCancelable(false) }
else runInSafe { instanceAndroid?.setCancelable(false) }
instance?.setCancelable(false)
}
/** 设置对话框标题 */
var title
get() = ""
set(value) {
if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setTitle(value) }
else runInSafe { instanceAndroid?.setTitle(value) }
instance?.setTitle(value)
}
/** 设置对话框消息内容 */
var msg
get() = ""
set(value) {
if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setMessage(value) }
else runInSafe { instanceAndroid?.setMessage(value) }
instance?.setMessage(value)
}
/** 设置进度条对话框消息内容 */
@@ -124,7 +127,10 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
customLayoutView = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER or Gravity.START
addView(ProgressBar(context))
addView(CircularProgressIndicator(context).apply {
isIndeterminate = true
trackCornerRadius = 10.dp(context)
})
addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
addView(TextView(context).apply {
tag = "progressContent"
@@ -135,75 +141,52 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
else customLayoutView?.findViewWithTag<TextView>("progressContent")?.text = value
}
/**
* 设置对话框自定义布局
* @return [ViewBinding]
*/
inline fun <reified T : ViewBinding> bind() =
T::class.java.method {
name = "inflate"
param(LayoutInflaterClass)
}.get().invoke<T>(LayoutInflater.from(context))?.apply {
customLayoutView = root
} ?: error("binding failed")
/**
* 设置对话框确定按钮
* @param text 按钮文本内容
* @param it 点击事件
* @param callback 点击事件
*/
fun confirmButton(text: String = "确定", it: () -> Unit = {}) {
if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setPositiveButton(text) { _, _ -> it() } }
else runInSafe { instanceAndroid?.setPositiveButton(text) { _, _ -> it() } }
fun confirmButton(text: String = "确定", callback: () -> Unit = {}) {
instance?.setPositiveButton(text) { _, _ -> callback() }
}
/**
* 设置对话框取消按钮
* @param text 按钮文本内容
* @param it 点击事件
* @param callback 点击事件
*/
fun cancelButton(text: String = "取消", it: () -> Unit = {}) {
if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setNegativeButton(text) { _, _ -> it() } }
else runInSafe { instanceAndroid?.setNegativeButton(text) { _, _ -> it() } }
fun cancelButton(text: String = "取消", callback: () -> Unit = {}) {
instance?.setNegativeButton(text) { _, _ -> callback() }
}
/**
* 设置对话框第三个按钮
* @param text 按钮文本内容
* @param it 点击事件
* @param callback 点击事件
*/
fun neutralButton(text: String = "更多", it: () -> Unit = {}) {
if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setNeutralButton(text) { _, _ -> it() } }
else runInSafe { instanceAndroid?.setNeutralButton(text) { _, _ -> it() } }
fun neutralButton(text: String = "更多", callback: () -> Unit = {}) {
instance?.setNeutralButton(text) { _, _ -> callback() }
}
/**
* 当对话框关闭时
* @param callback 回调
*/
fun onCancel(callback: () -> Unit) {
onCancel = callback
}
/** 取消对话框 */
fun cancel() = dialogInstance?.cancel()
/** 显示对话框 */
internal fun show() =
if (isUsingAndroidX) runInSafe {
instanceAndroidX?.create()?.apply {
customLayoutView?.let { setView(it) }
dialogInstance = this
}?.show()
} else runInSafe {
instanceAndroid?.create()?.apply {
customLayoutView?.let { setView(it) }
window?.setBackgroundDrawable(
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
if (isUseBlackTheme) intArrayOf(0xFF2D2D2D.toInt(), 0xFF2D2D2D.toInt())
else intArrayOf(Color.WHITE, Color.WHITE)
).apply {
shape = GradientDrawable.RECTANGLE
gradientType = GradientDrawable.LINEAR_GRADIENT
cornerRadius = 15.dpFloat(this@DialogBuilder.context)
})
dialogInstance = this
}?.show()
}
fun show() = runInSafe {
/** 若当前自定义 View 的对话框没有调用 [binding] 将会对其手动调用一次以确保显示布局 */
if (bindingClass != null) binding
instance?.create()?.apply {
customLayoutView?.let { setView(it) }
dialogInstance = this
setOnCancelListener { onCancel?.invoke() }
}?.show()
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,13 +18,13 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/3/13.
* This file is created by fankes on 2022/3/13.
*/
@file:Suppress("unused")
package com.fankes.coloros.notify.utils.factory
import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.log.YLog
/**
* 忽略异常返回值
@@ -79,5 +79,5 @@ inline fun <T> safeOf(default: T, result: () -> T) = try {
* @param block 正常回调
*/
inline fun <T> T.runInSafe(msg: String = "", block: () -> Unit) {
runCatching(block).onFailure { if (msg.isNotBlank()) loggerE(msg = msg, e = it) }
runCatching(block).onFailure { if (msg.isNotBlank()) YLog.error(msg, it) }
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,9 +18,9 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/7.
* This file is created by fankes on 2022/1/7.
*/
@file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt")
@file:Suppress("unused", "ObsoleteSdkInt", "DEPRECATION")
package com.fankes.coloros.notify.utils.factory
@@ -28,15 +28,17 @@ import android.app.Activity
import android.app.Notification
import android.app.Service
import android.app.WallpaperManager
import android.app.WallpaperManager.FLAG_SYSTEM
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
@@ -48,20 +50,28 @@ import android.os.Handler
import android.provider.Settings
import android.util.Base64
import android.widget.Toast
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.getSystemService
import androidx.core.content.pm.PackageInfoCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.net.toUri
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.java.StringType
import com.highcapable.yukihookapi.hook.factory.toClassOrNull
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.java.StringClass
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
import com.topjohnwu.superuser.Shell
import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.Calendar
import java.util.Date
import java.util.Locale
/**
* 系统深色模式是否开启
@@ -87,6 +97,12 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
*/
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
/**
* 系统版本是否高于或等于 Android 13
* @return [Boolean] 是否符合条件
*/
inline val isUpperOfAndroidT get() = Build.VERSION.SDK_INT > Build.VERSION_CODES.S
/**
* 系统版本是否高于或等于 Android 12
* @return [Boolean] 是否符合条件
@@ -103,7 +119,7 @@ inline val isLowerAndroidP get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
* 当前设备是否是 ColorOS 定制 Android 系统
* @return [Boolean] 是否符合条件
*/
val isColorOS by lazy { ("oppo.R").hasClass || ("com.color.os.ColorBuild").hasClass || ("oplus.R").hasClass }
val isColorOS by lazy { "oppo.R".hasClass() || "com.color.os.ColorBuild".hasClass() || "oplus.R".hasClass() }
/**
* 当前设备是否不是 ColorOS 定制 Android 系统
@@ -118,7 +134,7 @@ inline val isNotColorOS get() = !isColorOS
val isRealmeUI
get() = safeOfFalse {
val query = appContext.contentResolver.query(
Uri.parse("content://com.oplus.customize.coreapp.configmanager.configprovider.AppFeatureProvider")
"content://com.oplus.customize.coreapp.configmanager.configprovider.AppFeatureProvider".toUri()
.buildUpon()
.appendPath("app_feature")
.build(), null, "featurename=?", arrayOf("com.android.launcher.device_rm"), null
@@ -128,6 +144,29 @@ val isRealmeUI
isExist
}
/**
* 获取 Android 版本代号
* @return [String]
*/
val androidVersionCodeName
get() = when (Build.VERSION.SDK_INT) {
34 -> "U"
33 -> "T"
32 -> "S_V2"
31 -> "S"
30 -> "R"
29 -> "Q"
28 -> "P"
27 -> "O_MR1"
26 -> "O"
25 -> "N_MR1"
24 -> "N"
23 -> "M"
22 -> "L_MR1"
21 -> "L"
else -> "?"
}
/**
* 获取 ColorOS 完整版本
* @return [String]
@@ -138,69 +177,102 @@ val colorOSFullVersion get() = "${if (isRealmeUI) "RealmeUI " else ""}$colorOSVe
* 获取 ColorOS 版本
* @return [String]
*/
val colorOSVersion
get() = safeOf(default = "无法获取") {
(classOf(name = "com.oplus.os.OplusBuild").let {
it.field { name = "VERSIONS" }.ignoredError().get().array<String>().takeIf { e -> e.isNotEmpty() }
?.get(it.method { name = "getOplusOSVERSION" }.ignoredError().get().int() - 1)
} ?: findPropString(
key = "ro.system.build.fingerprint", default = "无法获取"
).split("ssi:")[1].split("/")[0].trim()) + " ${Build.DISPLAY}"
}
val colorOSVersion get() = "$colorOSNumberVersion ${Build.DISPLAY}"
/**
* 得到安装包信息
* @return [PackageInfo]
*/
val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?: PackageInfo()
/**
* 判断应用是否安装
* @return [Boolean]
*/
val String.isInstall
get() = safeOfFalse {
appContext.packageManager.getPackageInfo(
this, PackageManager.GET_UNINSTALLED_PACKAGES
)
true
}
/**
* 得到版本信息
* 获取 ColorOS 数字版本
* @return [String]
*/
val Context.versionName get() = packageInfo.versionName ?: ""
val colorOSNumberVersion
get() = safeOf(default = "无法获取") {
"com.oplus.os.OplusBuild".toClassOrNull()?.let {
it.field { name = "VERSIONS" }.ignored().get().array<String>().takeIf { e -> e.isNotEmpty() }
?.get(it.method { name = "getOplusOSVERSION" }.ignored().get().int() - 1)
} ?: findPropString("ro.system.build.fingerprint", "无法获取").split("ssi:")[1].split("/")[0].trim()
}
/**
* 得到版本号
* 获取 [Drawable]
* @param resId 属性资源 ID
* @return [Drawable]
*/
fun Resources.drawableOf(@DrawableRes resId: Int) = ResourcesCompat.getDrawable(this, resId, null) ?: error("Invalid resources")
/**
* 获取颜色
* @param resId 属性资源 ID
* @return [Int]
*/
val Context.versionCode get() = packageInfo.versionCode
fun Resources.colorOf(@ColorRes resId: Int) = ResourcesCompat.getColor(this, resId, null)
/**
* 得到 APP 安装包信息 (兼容)
* @param packageName APP 包名
* @param flag [PackageInfoFlags]
* @return [PackageInfo] or null
*/
private fun Context.getPackageInfoCompat(packageName: String, flag: Number = 0) = runCatching {
@Suppress("DEPRECATION", "KotlinRedundantDiagnosticSuppress")
if (Build.VERSION.SDK_INT >= 33)
packageManager?.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong()))
else packageManager?.getPackageInfo(packageName, flag.toInt())
}.getOrNull()
/**
* 得到 APP 版本号 (兼容 [PackageInfo.getLongVersionCode])
* @return [Int]
*/
private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this)
/**
* 判断 APP 是否安装
* @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isInstall(packageName: String) = getPackageInfoCompat(packageName)?.let { true } ?: false
/**
* 得到 APP 版本信息
* @return [String]
*/
val Context.appVersionName get() = getPackageInfoCompat(packageName)?.versionName ?: ""
/**
* 得到 APP 版本号
* @return [Int]
*/
val Context.appVersionCode get() = getPackageInfoCompat(packageName)?.versionCodeCompat
/**
* 得到 APP 名称
* @param name APP 包名
* @param packageName APP 包名 - 默认为当前 APP
* @return [String]
*/
fun Context.findAppName(name: String) =
safeOfNothing { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadLabel(packageManager).toString() }
fun Context.appNameOf(packageName: String = getPackageName()) =
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
/**
* 得到 APP 图标
* @param name APP 包名
* @param packageName APP 包名 - 默认为当前 APP
* @return [Drawable] or null
*/
fun Context.findAppIcon(name: String) =
safeOfNull { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadIcon(packageManager) }
fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
/**
* 获取 APP 是否为 DEBUG 版本
* @param name APP 包名
* @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isAppDebuggable(name: String) =
safeOfFalse { (packageManager?.getPackageInfo(name, 0)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 }
fun Context.isDebugApp(packageName: String) =
safeOfFalse { (getPackageInfoCompat(packageName)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 }
/**
* 获取 APP 是否为系统 APP
* @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isSystemApp(packageName: String) =
safeOfFalse { (getPackageInfoCompat(packageName)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) ?: 0) != 0 }
/**
* 对数值自动补零
@@ -240,8 +312,11 @@ val isNotNoificationEnabled get() = !NotificationManagerCompat.from(appContext).
* 网络连接是否正常
* @return [Boolean] 网络是否连接
*/
val isNetWorkSuccess
get() = safeOfFalse { appContext.getSystemService<ConnectivityManager>()?.activeNetworkInfo != null }
val Context.isNetWorkSuccess
get() = safeOfFalse {
@Suppress("DEPRECATION")
getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
}
/**
* dp 转换为 pxInt
@@ -258,21 +333,32 @@ fun Number.dp(context: Context) = dpFloat(context).toInt()
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
/**
* 获取系统壁纸颜
* 获取系统主题
*
* 由于 ColorOS 阉割了 [android.R.color.system_accent1_600] 这里取系统壁纸颜色做补偿
* @return [Int] 无法获取时返回透明色
*
* 从 ColorOS 13 (Android 13) 开始使用系统取色方案
* @return [Int] Android < 13 返回 [wallpaperColor]
*/
val Context.systemAccentColor
get() = safeOf(wallpaperColor) {
if (isUpperOfAndroidT) resources.colorOf(android.R.color.system_accent1_600) else wallpaperColor
}
/**
* 获取系统壁纸颜色
* @return [Int] 无法获取时返回默认颜色
*/
val Context.wallpaperColor
get() = safeOfNan {
WallpaperManager.getInstance(this).getWallpaperColors(FLAG_SYSTEM)?.secondaryColor?.toArgb() ?: 0
}
get() = runCatching {
WallpaperManager.getInstance(this).getWallpaperColors(WallpaperManager.FLAG_SYSTEM)?.primaryColor?.toArgb()
}.getOrNull() ?: (if (isSystemInDarkMode) 0xFFD8D8D8.toInt() else 0xFF707173.toInt())
/**
* 是否为白色
* @return [Boolean]
*/
val Int.isWhite
val Int.isWhiteColor
get() = safeOfTrue {
val r = this and 0xff0000 shr 16
val g = this and 0x00ff00 shr 8
@@ -280,6 +366,13 @@ val Int.isWhite
(0.2126 * r + 0.7152 * g + 0.0722 * b) >= 128
}
/**
* 调整颜色透明度
* @param value 透明度
* @return [Int] 调整后的颜色
*/
fun Int.colorAlphaOf(value: Float) = safeOfNan { (255.coerceAtMost(0.coerceAtLeast((value * 255).toInt())) shl 24) + (0x00ffffff and this) }
/**
* Base64 加密
* @return [String]
@@ -322,17 +415,20 @@ val String.bitmap: Bitmap get() = unbase64.bitmap
* @return [String]
*/
fun findPropString(key: String, default: String = "") = safeOf(default) {
(classOf(name = "android.os.SystemProperties").method {
"android.os.SystemProperties".toClassOrNull()?.method {
name = "get"
param(StringType, StringType)
}.get().invoke(key, default)) ?: default
param(StringClass, StringClass)
}?.get()?.invoke(key, default) ?: default
}
/**
* 是否有 Root 权限
* @return [Boolean]
*/
val isRootAccess get() = safeOfFalse { Shell.rootAccess() }
val isRootAccess get() = safeOfFalse {
@Suppress("DEPRECATION")
Shell.rootAccess()
}
/**
* 执行命令
@@ -341,6 +437,7 @@ val isRootAccess get() = safeOfFalse { Shell.rootAccess() }
* @return [String] 执行结果
*/
fun execShell(cmd: String, isSu: Boolean = true) = safeOfNothing {
@Suppress("DEPRECATION")
(if (isSu) Shell.su(cmd) else Shell.sh(cmd)).exec().out.let {
if (it.isNotEmpty()) it[0].trim() else ""
}
@@ -350,34 +447,34 @@ fun execShell(cmd: String, isSu: Boolean = true) = safeOfNothing {
* 弹出 [Toast]
* @param msg 提示内容
*/
fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).show()
fun toast(msg: String) {
runCatching {
Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).show()
}.onFailure { YLog.warn(msg) }
}
/**
* 跳转到指定页面
*
* [T] 为指定的 [Activity]
*/
inline fun <reified T : Activity> Context.navigate() = runInSafe { startActivity(Intent(this, T::class.java)) }
/**
* 跳转到指定页面
*
* [T] 为指定的 [Activity]
*/
inline fun <reified T : Activity> Service.navigate() =
runInSafe { startActivity(Intent(this, T::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }) }
inline fun <reified T : Activity> Context.navigate() = runInSafe {
startActivity(Intent(if (this is Service) applicationContext else this, T::class.java).apply {
if (this@navigate !is Activity) flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}
/**
* 弹出 [Snackbar]
* @param msg 提示内容
* @param actionText 按钮文本 - 不写默认取消按钮
* @param it 按钮事件回调
* @param callback 按钮事件回调
*/
fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) =
fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {}) =
Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).apply {
if (actionText.isBlank()) return@apply
setActionTextColor(if (isSystemInDarkMode) Color.BLACK else Color.WHITE)
setAction(actionText) { it() }
setAction(actionText) { callback() }
}.show()
/**
@@ -389,7 +486,7 @@ fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
startActivity(Intent().apply {
if (packageName.isNotBlank()) setPackage(packageName)
action = Intent.ACTION_VIEW
data = Uri.parse(url)
data = url.toUri()
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
@@ -403,7 +500,7 @@ fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
* @param packageName 包名
*/
fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching {
if (packageName.isInstall)
if (isInstall(packageName))
startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
@@ -448,4 +545,30 @@ fun Long.stampToDate(format: String = "yyyy-MM-dd HH:mm:ss") =
* @param ms 毫秒 - 默认150
* @param it 方法体
*/
fun Any?.delayedRun(ms: Long = 150, it: () -> Unit) = runInSafe { Handler().postDelayed({ it() }, ms) }
fun Any?.delayedRun(ms: Long = 150, it: () -> Unit) = runInSafe {
@Suppress("DEPRECATION")
Handler().postDelayed({ it() }, ms)
}
/**
* 隐藏或显示启动器图标
*
* - 你可能需要 LSPosed 的最新版本以开启高版本系统中隐藏 APP 桌面图标功能
* @param isShow 是否显示
*/
fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
packageManager?.setComponentEnabledSetting(
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home"),
if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
)
}
/**
* 获取启动器图标状态
* @return [Boolean] 是否显示
*/
val Context.isLauncherIconShowing
get() = packageManager?.getComponentEnabledSetting(
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home")
) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED

View File

@@ -0,0 +1,84 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/4/17.
*/
package com.fankes.coloros.notify.utils.tool
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Icon
import android.os.Build
import androidx.core.graphics.drawable.toBitmap
import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.utils.factory.appIconOf
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
/**
* 模块更新激活提醒通知工具类
*/
object ActivationPromptTool {
/** 当前模块的包名 */
private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID
/** 推送通知的渠道名称 */
private const val NOTIFY_CHANNEL = "activationPromptId"
/**
* 推送提醒通知
* @param context 当前实例
* @param packageName 当前 APP 包名
*/
fun prompt(context: Context, packageName: String) {
if (packageName != BuildConfigWrapper.APPLICATION_ID) return
context.getSystemService(NotificationManager::class.java)?.apply {
createNotificationChannel(
NotificationChannel(
NOTIFY_CHANNEL, "ColorOS 通知图标增强 - 版本更新",
NotificationManager.IMPORTANCE_DEFAULT
).apply { enableLights(false) }
)
notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply {
setShowWhen(true)
setContentTitle("模块已更新")
setContentText("点按通知打开模块以完成新版本激活。")
setColor(0xFF4E8A5A.toInt())
setAutoCancel(true)
setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_notify_update))
setLargeIcon(context.appIconOf(packageName)?.toBitmap())
setContentIntent(
PendingIntent.getActivity(
context, packageName.hashCode(),
Intent().apply {
component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.MainActivity")
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
)
)
}.build())
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/1/28.
*/
package com.fankes.coloros.notify.utils.tool
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.drawable.AnimationDrawable
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.VectorDrawable
import android.util.ArrayMap
import androidx.core.graphics.drawable.toBitmap
import com.fankes.coloros.notify.utils.factory.safeOfFalse
import kotlin.math.abs
/**
* 这是一个从 AOSP 源码中分离出来的功能
*
* 主要作用于兼容部分第三方系统修改颜色判断代码造成判断位图灰度功能失效
*/
object BitmapCompatTool {
/** 缓存已判断的结果防止卡顿 */
private var cachedBitmapGrayscales = ArrayMap<Int, Boolean>()
private var tempBuffer = intArrayOf(0)
private var tempCompactBitmap: Bitmap? = null
private var tempCompactBitmapCanvas: Canvas? = null
private var tempCompactBitmapPaint: Paint? = null
private val tempMatrix = Matrix()
/**
* 判断 [Drawable] 是否为灰度位图
* @param drawable 要判断的 [Drawable]
* @return [Boolean] 是否灰度
*/
fun isGrayscaleDrawable(drawable: Drawable) = safeOfFalse {
when (drawable) {
is BitmapDrawable -> isGrayscaleBitmap(drawable.bitmap)
is AnimationDrawable -> !(drawable.numberOfFrames <= 0 || !isGrayscaleBitmap(drawable.getFrame(0).toBitmap()))
is VectorDrawable -> true
else -> isGrayscaleBitmap(drawable.toBitmap())
}
}
/**
* 判断 [Bitmap] 是否为灰度位图
* @param bitmap 要判断的位图
* @return [Boolean] 是否灰度
*/
private fun isGrayscaleBitmap(bitmap: Bitmap) =
cachedBitmapGrayscales[bitmap.generationId] ?: let {
var height = bitmap.height
var width = bitmap.width
if (height > 64 || width > 64) {
if (tempCompactBitmap == null) {
tempCompactBitmap = Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888)
.also { tempCompactBitmapCanvas = Canvas(it) }
tempCompactBitmapPaint = Paint(Paint.FILTER_BITMAP_FLAG).apply { isFilterBitmap = true }
}
tempMatrix.reset()
tempMatrix.setScale(64f / width, 64f / height, 0f, 0f)
tempCompactBitmapCanvas?.drawColor(0, PorterDuff.Mode.SRC)
tempCompactBitmapCanvas?.drawBitmap(bitmap, tempMatrix, tempCompactBitmapPaint)
height = 64
width = 64
}
val size = height * width
ensureBufferSize(size)
tempCompactBitmap?.getPixels(tempBuffer, 0, width, 0, 0, width, height)
for (i in 0 until size)
if (isGrayscaleColor(tempBuffer[i]).not()) {
cachedBitmapGrayscales[bitmap.generationId] = false
return@let false
}
cachedBitmapGrayscales[bitmap.generationId] = true
true
}
/**
* 提纯 [Bitmap] 颜色判断灰度
* @param color 颜色
* @return [Boolean] 是否灰度
*/
private fun isGrayscaleColor(color: Int): Boolean {
if (color shr 24 and 255 < 50) return true
val r = color shr 16 and 255
val g = color shr 8 and 255
val b = color and 255
return !(abs(r - g) >= 20 || abs(r - b) >= 20 || abs(g - b) >= 20)
}
/**
* 计算字节数组
* @param size 大小
*/
private fun ensureBufferSize(size: Int) {
if (tempBuffer.size < size) tempBuffer = IntArray(size)
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,20 +18,32 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/3/20.
* This file is created by fankes on 2022/3/20.
*/
package com.fankes.coloros.notify.utils.tool
import android.app.Activity
import android.content.Context
import com.fankes.coloros.notify.utils.factory.*
import okhttp3.*
import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar
import android.icu.util.TimeZone
import com.fankes.coloros.notify.utils.factory.isNetWorkSuccess
import com.fankes.coloros.notify.utils.factory.openBrowser
import com.fankes.coloros.notify.utils.factory.openSelfSetting
import com.fankes.coloros.notify.utils.factory.runInSafe
import com.fankes.coloros.notify.utils.factory.showDialog
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.json.JSONObject
import java.io.IOException
import java.io.Serializable
import java.util.Locale
/**
* 获取 Github Release 最新版本工具类
* 获取 GitHub Release 最新版本工具类
*/
object GithubReleaseTool {
@@ -45,9 +57,9 @@ object GithubReleaseTool {
* 获取最新版本信息
* @param context 实例
* @param version 当前版本
* @param it 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
* @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
*/
fun checkingForUpdate(context: Context, version: String, it: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
OkHttpClient().newBuilder().build().newCall(
Request.Builder()
.url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest")
@@ -57,23 +69,23 @@ object GithubReleaseTool {
override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) = runInSafe {
JSONObject(response.body?.string() ?: "").apply {
JSONObject(response.body.string()).apply {
GithubReleaseBean(
name = getString("name"),
htmlUrl = getString("html_url"),
content = getString("body"),
date = getString("published_at").replace(oldValue = "T", newValue = " ").replace(oldValue = "Z", newValue = "")
date = getString("published_at").localTime()
).apply {
fun showUpdate() = context.showDialog {
title = "最新版本 $name"
msg = "发布于 $date\n\n" +
"更新日志\n\n" + content
"更新日志\n\n" + content
confirmButton(text = "更新") { context.openBrowser(htmlUrl) }
cancelButton()
}
if (name != version) (context as? Activity?)?.runOnUiThread {
showUpdate()
it(name) { showUpdate() }
result(name) { showUpdate() }
}
}
}
@@ -84,10 +96,10 @@ object GithubReleaseTool {
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
* @param result 已连接回调
*/
private fun checkingInternetConnect(context: Context, it: () -> Unit) = runInSafe {
if (isNetWorkSuccess)
private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe {
if (context.isNetWorkSuccess)
OkHttpClient().newBuilder().build().newCall(
Request.Builder()
.url("https://www.baidu.com")
@@ -98,7 +110,7 @@ object GithubReleaseTool {
(context as? Activity?)?.runOnUiThread {
context.showDialog {
title = "网络不可用"
msg = "模块的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
confirmButton(text = "去开启") { context.openSelfSetting() }
cancelButton()
noCancelable()
@@ -107,13 +119,25 @@ object GithubReleaseTool {
}
override fun onResponse(call: Call, response: Response) = runInSafe {
(context as? Activity?)?.runOnUiThread { runInSafe { it() } }
(context as? Activity?)?.runOnUiThread { runInSafe { result() } }
}
})
}
/**
* Github Release bean
* 格式化时间为本地时区
* @return [String] 本地时区时间
*/
private fun String.localTime() = replace("T", " ").replace("Z", "").let {
runCatching {
val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
local.format(current.parse(it))
}.getOrNull() ?: it
}
/**
* GitHub Release bean
* @param name 版本名称
* @param htmlUrl 网页地址
* @param content 更新日志

View File

@@ -0,0 +1,55 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* <p>
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/2/3.
*/
package com.fankes.coloros.notify.utils.tool
import android.content.Context
import com.fankes.coloros.notify.utils.factory.showDialog
import com.highcapable.yukihookapi.hook.factory.prefs
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
import java.util.Locale
/**
* I18n 适配警告提示工具类
*/
object I18nWarnTool {
/** 推广已读存储键值 */
private val LOCALE_WARN_READED = PrefsData("locale_warn_readed", false)
/**
* 检查并显示 I18n 适配警告对话框
* @param context 实例
*/
fun checkingOrShowing(context: Context) {
fun saveReaded() = context.prefs().edit { put(LOCALE_WARN_READED, value = true) }
if (Locale.getDefault().language.startsWith("zh").not() && context.prefs().get(LOCALE_WARN_READED).not())
context.showDialog {
title = "Notice of I18n Support"
msg = "This Xposed Module is only for Chinese and the Chinese region.\n\n" +
"Currently, there will be no internationalization adaptation.\n\n" +
"There may be plans for internationalization adaptation in the future, so stay tuned."
confirmButton(text = "Got It") { saveReaded() }
noCancelable()
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,7 +18,7 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/3/20.
* This file is created by fankes on 2022/3/20.
*/
package com.fankes.coloros.notify.utils.tool
@@ -29,13 +29,18 @@ import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Icon
import android.os.Build
import androidx.core.graphics.drawable.toBitmap
import com.fankes.coloros.notify.const.Const
import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.hook.HookEntry
import com.fankes.coloros.notify.utils.factory.*
import com.fankes.coloros.notify.utils.factory.appIconOf
import com.fankes.coloros.notify.utils.factory.appNameOf
import com.fankes.coloros.notify.utils.factory.isDebugApp
import com.fankes.coloros.notify.utils.factory.isSystemApp
import com.fankes.coloros.notify.utils.factory.runInSafe
import com.fankes.coloros.notify.utils.factory.stampToDate
import com.fankes.coloros.notify.wrapper.BuildConfigWrapper
/**
* 通知图标适配推送通知类
@@ -44,71 +49,14 @@ import com.fankes.coloros.notify.utils.factory.*
*/
object IconAdaptationTool {
/** 当前模块的包名 */
private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID
/** 推送通知的渠道名称 */
private const val NOTIFY_CHANNEL = "notifyRuleSupportId"
/** 已过期的日期 */
private val outDateLimits = HashSet<String>()
/**
* 使用的小图标
* @return [Bitmap]
*/
private val smallIcon by lazy {
("iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAG\n" +
"x2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkP\n" +
"SJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG\n" +
"9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDYgNzkuZGF\n" +
"iYWNiYiwgMjAyMS8wNC8xNC0wMDozOTo0NCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6\n" +
"cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gP\n" +
"HJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYW\n" +
"RvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1\n" +
"lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90\n" +
"b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuM\n" +
"C9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cG\n" +
"UvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI\n" +
"yLjQgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTAzLTIzVDAyOjU1OjM2\n" +
"KzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wMy0yM1QwMzowMDoyMSswODowMCIge\n" +
"G1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0wMy0yM1QwMzowMDoyMSswODowMCIgZGM6Zm9ybW\n" +
"F0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0N\n" +
"Qcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlp\n" +
"ZDpiZGY1YWY2NS1lYjYxLTRiOGUtOTk1NS01OTE5OThkN2ExNDAiIHhtcE1NOkRvY3VtZ\n" +
"W50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2MDFlYjQ1OC01ZTdlLWFjNDYtODA0Mi\n" +
"1iNWJmYTFhYWQwNGMiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYTU\n" +
"4YTA1ZS04OTU1LTQyNzItODg2NC0xNmI5MWI0YzcxMmUiPiA8eG1wTU06SGlzdG9yeT4g\n" +
"PHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0Y\n" +
"W5jZUlEPSJ4bXAuaWlkOmNhNThhMDVlLTg5NTUtNDI3Mi04ODY0LTE2YjkxYjRjNzEyZS\n" +
"Igc3RFdnQ6d2hlbj0iMjAyMi0wMy0yM1QwMjo1NTozNiswODowMCIgc3RFdnQ6c29mdHd\n" +
"hcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIyLjQgKE1hY2ludG9zaCkiLz4gPHJkZjps\n" +
"aSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjg5Y\n" +
"mIxNTI1LWVjYmMtNGY2NC1iZTRlLWU1N2EyNjQ1NDE1YyIgc3RFdnQ6d2hlbj0iMjAyMi\n" +
"0wMy0yM1QwMjo1NzoxMCswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGh\n" +
"vdG9zaG9wIDIyLjQgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjps\n" +
"aSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJkZ\n" +
"jVhZjY1LWViNjEtNGI4ZS05OTU1LTU5MTk5OGQ3YTE0MCIgc3RFdnQ6d2hlbj0iMjAyMi\n" +
"0wMy0yM1QwMzowMDoyMSswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGh\n" +
"vdG9zaG9wIDIyLjQgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6\n" +
"U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+I\n" +
"DwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+QdGsggAAA35JREFUaIHtmE2IVW\n" +
"Ucxn9z0Ukm7cMSo1AxUwjNIclLRVSL0CAoyD5oYwS1iogWGW0rwQpcJNjCRdKmTR+SBhF\n" +
"BWWZlZllmOgsT/EqHKRuqMVv8XJy5zPGd95x75hyh03Ae+C/O//2/z3Oee97P26MyGdD6\n" +
"r1/gQqExUjc0RuqGxkjd0BipGxojdcOkMTKlS/tCYBXQBvqBWcA/wI/AD8AW4NML9C79w\n" +
"APAYmAOcCUwCBwCvgTeB37J7K3Goq1utRgG1NUZPEXibnVnQa231CUxnhjxmoKkId5WWx\n" +
"M08XpJrSe7GXmlJHEH36l9oUgketRPKmqtS3P2OHaxWgO8HBl9vwPbgM+AU8B0YAnJ3Fk\n" +
"Uqd8HLAfO5MyHz4HbIvkh4ENgJ/Dn6DxZCdxFfGF6GngtPUeuz3D9qnpZzi/7mDoS6fe9\n" +
"Oi1SP0XdnqH1vDo9Q2eeujmj33WmhtbuSMFTOQbSsUg9Gem/3/HDbEuk7ox6c0GtZyP9d\n" +
"3WMLIg0vlGQuBPz1cEIzx51aqouxN/qsglqvRnhWYj6UpAcVnsnSN75/MciIntTfC+m8r\n" +
"+qi0vo9Kp/BBrrUD8KkutLkHdijvpbxMy3jn2Z1eoL6uUVdDYE/NtRjwfJ+yoIoF6rnoi\n" +
"Y2VORNx33B9wHWsDMYEk7m7NsFsEhYBlwMsjfCOwFplbkBzgdPPe2gOEg2e38VQQnSMwM\n" +
"BvmlwBfAtIr8FwXPIy1gf5BsVxS5ieTQd5zEzFDQvhzYBfRU0OgPnk/j+GPJYMlx26d+P\n" +
"MpxNjXX5hlfmneYbJBltAYCrk2YnCZDnHeOKRAXqz8FHMOp9mtMltsQu0uYeTzC085yqL\n" +
"qqIPFMdV+k/46gbr7xpflrz98086Kt/hv0P6JjR5RbIwKqz5h/NF/p+OVb9bA6O1I/N6P\n" +
"+Z/WWLiYezXjHO9JGMBlOMRxW16r3mhwn7jS5D2Rdho6ps3JeKGuYaXKnedhkL5qhLlWf\n" +
"UL/KqN/Y4Q1FNmd0KIqj6tU5JtJmjnThGu7S/k6aMyayvpSF5FfL+xJhXKF+U1JrU8iXJ\n" +
"bLC7M8Z4pTJXJrIypOO59ShgloH1YdiPOkbYgy3A48ANwBXAZcCIyQ79gDwAfAe8FeFzQ\n" +
"3gEuBB4B5gATAb6BvlPUpy63wX2JpF0M3I/waT5g+6xkjd0BipGxojdUNjpG5ojNQNjZG\n" +
"64Rx5J7QeMy++3AAAAABJRU5ErkJggg==").bitmap
}
/** 已过期的时间 */
private val outTimeLimits = HashSet<String>()
/**
* 推送新 APP 安装适配通知
@@ -116,7 +64,8 @@ object IconAdaptationTool {
* @param packageName 安装的 APP 包名
*/
fun pushNewAppSupportNotify(context: Context, packageName: String) {
if (context.isAppDebuggable(packageName)) return
if (packageName.startsWith("com.google.android.trichromelibrary")) return
if (context.isSystemApp(packageName) || context.isDebugApp(packageName)) return
context.getSystemService(NotificationManager::class.java)?.apply {
createNotificationChannel(
NotificationChannel(
@@ -126,24 +75,24 @@ object IconAdaptationTool {
)
notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply {
setShowWhen(true)
setContentTitle("您已安装 ${context.findAppName(packageName)}")
setContentTitle("您已安装 ${context.appNameOf(packageName)}")
setContentText("尚未适配此应用,点按打开在线规则。")
setColor(0xFF2993F0.toInt())
setAutoCancel(true)
setSmallIcon(Icon.createWithBitmap(smallIcon))
setLargeIcon(context.findAppIcon(packageName)?.toBitmap())
setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_unsupported))
setLargeIcon(context.appIconOf(packageName)?.toBitmap())
setContentIntent(
PendingIntent.getActivity(
context, packageName.hashCode(),
Intent().apply {
component = ComponentName(
Const.MODULE_PACKAGE_NAME,
"${Const.MODULE_PACKAGE_NAME}.ui.activity.ConfigureActivity"
MODULE_PACKAGE_NAME,
"$MODULE_PACKAGE_NAME.ui.activity.ConfigureActivity"
)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}.apply {
putExtra("isNewAppSupport", true)
putExtra("appName", context.findAppName(packageName))
putExtra("appName", context.appNameOf(packageName))
putExtra("pkgName", packageName)
}, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
)
@@ -163,27 +112,20 @@ object IconAdaptationTool {
/**
* 自动更新通知图标优化在线规则
*
* 一天执行一次
* @param context 实例
* @param timeSet 设定的时间
*/
fun prepareAutoUpdateIconRule(context: Context, timeSet: String) = runInSafe {
System.currentTimeMillis().also {
if (it.stampToDate(format = "HH:mm") == timeSet && (outDateLimits.isEmpty() || outDateLimits.none { each ->
each == it.stampToDate(format = "yyyy-MM-dd")
})) {
outDateLimits.add(it.stampToDate(format = "yyyy-MM-dd"))
context.startActivity(
Intent().apply {
component = ComponentName(
Const.MODULE_PACKAGE_NAME,
"${Const.MODULE_PACKAGE_NAME}.ui.activity.auto.NotifyIconRuleUpdateActivity"
)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
)
}
val nowTime = it.stampToDate(format = "HH:mm")
if (timeSet != nowTime || outTimeLimits.any { e -> e == nowTime }) return
outTimeLimits.add(nowTime)
context.startActivity(
Intent().apply {
component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.auto.NotifyIconRuleUpdateActivity")
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
)
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,9 +18,9 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/2/25.
* This file is created by fankes on 2022/2/25.
*/
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "DEPRECATION", "IMPLICIT_CAST_TO_ANY")
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager", "IMPLICIT_CAST_TO_ANY")
package com.fankes.coloros.notify.utils.tool
@@ -39,24 +39,31 @@ import androidx.core.content.getSystemService
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.data.DataConst
import com.fankes.coloros.notify.const.IconRuleSourceSyncType
import com.fankes.coloros.notify.data.ConfigData
import com.fankes.coloros.notify.databinding.DiaSourceFromBinding
import com.fankes.coloros.notify.databinding.DiaSourceFromStringBinding
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_1
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_2
import com.fankes.coloros.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_3
import com.fankes.coloros.notify.param.IconPackParams
import com.fankes.coloros.notify.ui.activity.ConfigureActivity
import com.fankes.coloros.notify.utils.factory.delayedRun
import com.fankes.coloros.notify.utils.factory.openBrowser
import com.fankes.coloros.notify.utils.factory.safeOfNull
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.snake
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.log.loggerD
import okhttp3.*
import com.highcapable.yukihookapi.hook.log.YLog
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.IOException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.*
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
/**
* 通知图标在线规则管理类
@@ -70,65 +77,90 @@ object IconRuleManagerTool {
private const val OS_TAG = "ColorOS"
/** 当前规则的通知图标颜色 */
private const val OS_COLOR = 0xFF4E8A5A
private const val OS_COLOR = 0xFF4E8A5A.toInt()
/** 同步地址 - GitHub Raw (直连) */
private const val SYNC_DIRECT_URL = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main"
/** 同步地址 - GitHub Raw (代理 - GitHub Proxy) */
private const val SYNC_PROXY_1_URL = "https://ghp.ci/$SYNC_DIRECT_URL"
/** 同步地址 - GitHub Raw (代理 - 7ED Services) */
private const val SYNC_PROXY_2_URL = "https://raw.githubusercontentS.com/fankes/AndroidNotifyIconAdapt/main"
/** 云端规则展示地址 (OS) */
private const val RULES_TRAVELER_OS_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?notify-rules-coloros"
/** 云端规则展示地址 (APP) */
private const val RULES_TRAVELER_APP_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?notify-rules-app"
/** 请求适配通知图标优化名单地址 */
internal const val RULES_FEEDBACK_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?feedback"
/** 贡献通知图标优化名单地址 */
internal const val RULES_CONTRIBUTING_URL = "https://fankes.github.io/AndroidNotifyIconAdapt/?contribute"
/**
* 从在线地址手动同步规则
* @param context 实例
* @param it 成功后回调
* @param callback 成功后回调
*/
fun syncByHand(context: Context, it: () -> Unit) =
context.showDialog {
fun syncByHand(context: Context, callback: () -> Unit) =
context.showDialog<DiaSourceFromBinding> {
title = "同步列表"
var sourceType = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY)
var customUrl = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY_CUSTOM_URL)
bind<DiaSourceFromBinding>().apply {
diaSfText.apply {
if (customUrl.isNotBlank()) {
setText(customUrl)
setSelection(customUrl.length)
}
doOnTextChanged { text, _, _, _ -> customUrl = text.toString() }
}
diaSfTextLin.isVisible = sourceType == TYPE_SOURCE_SYNC_WAY_3
diaSfRd1.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_1
diaSfRd2.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_2
diaSfRd3.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_3
diaSfRd1.setOnClickListener {
diaSfRd2.isChecked = false
diaSfRd3.isChecked = false
diaSfTextLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_1
}
diaSfRd2.setOnClickListener {
diaSfRd1.isChecked = false
diaSfRd3.isChecked = false
diaSfTextLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_2
}
diaSfRd3.setOnClickListener {
diaSfRd1.isChecked = false
diaSfRd2.isChecked = false
diaSfTextLin.isVisible = true
sourceType = TYPE_SOURCE_SYNC_WAY_3
var sourceType = ConfigData.iconRuleSourceSyncType
var customUrl = ConfigData.iconRuleSourceSyncCustomUrl
binding.sourceUrlEdit.apply {
if (customUrl.isNotBlank()) {
setText(customUrl)
setSelection(customUrl.length)
}
doOnTextChanged { text, _, _, _ -> customUrl = text.toString() }
}
binding.sourceFromTextLin.isVisible = sourceType == IconRuleSourceSyncType.CUSTOM_URL
binding.sourceTravelerLin.isVisible = sourceType != IconRuleSourceSyncType.CUSTOM_URL
binding.sourceRadio0.isChecked = sourceType == IconRuleSourceSyncType.GITHUB_RAW_PROXY_1
binding.sourceRadio1.isChecked = sourceType == IconRuleSourceSyncType.GITHUB_RAW_PROXY_2
binding.sourceRadio2.isChecked = sourceType == IconRuleSourceSyncType.GITHUB_RAW_DIRECT
binding.sourceRadio3.isChecked = sourceType == IconRuleSourceSyncType.CUSTOM_URL
binding.sourceRadio0.setOnClickListener {
binding.sourceFromTextLin.isVisible = false
binding.sourceTravelerLin.isVisible = true
sourceType = IconRuleSourceSyncType.GITHUB_RAW_PROXY_1
}
binding.sourceRadio1.setOnClickListener {
binding.sourceFromTextLin.isVisible = false
binding.sourceTravelerLin.isVisible = true
sourceType = IconRuleSourceSyncType.GITHUB_RAW_PROXY_2
}
binding.sourceRadio2.setOnClickListener {
binding.sourceFromTextLin.isVisible = false
binding.sourceTravelerLin.isVisible = true
sourceType = IconRuleSourceSyncType.GITHUB_RAW_DIRECT
}
binding.sourceRadio3.setOnClickListener {
binding.sourceFromTextLin.isVisible = true
binding.sourceTravelerLin.isVisible = false
sourceType = IconRuleSourceSyncType.CUSTOM_URL
}
binding.sourceTravelerOsButton.setOnClickListener { context.openBrowser(RULES_TRAVELER_OS_URL) }
binding.sourceTravelerAppButton.setOnClickListener { context.openBrowser(RULES_TRAVELER_APP_URL) }
confirmButton {
context.modulePrefs.put(DataConst.SOURCE_SYNC_WAY, sourceType)
context.modulePrefs.put(DataConst.SOURCE_SYNC_WAY_CUSTOM_URL, customUrl)
sync(context, sourceType, customUrl, it)
ConfigData.iconRuleSourceSyncType = sourceType
ConfigData.iconRuleSourceSyncCustomUrl = customUrl
sync(context, sourceType, customUrl, callback)
}
cancelButton()
neutralButton(text = "自定义规则") {
context.showDialog {
title = "自定义规则(调试)"
val editText = bind<DiaSourceFromStringBinding>().diaSfsInputEdit.apply {
context.showDialog<DiaSourceFromStringBinding> {
title = "自定义规则 (调试)"
binding.jsonRuleEdit.apply {
requestFocus()
invalidate()
}
IconPackParams(context).also { params ->
confirmButton(text = "合并") {
editText.text.toString().also { jsonString ->
binding.jsonRuleEdit.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> context.snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
@@ -139,20 +171,20 @@ object IconRuleManagerTool {
)
)
notifyRefresh(context)
it()
callback()
}
else -> context.snake(msg = "请输入有效内容")
}
}
}
cancelButton(text = "覆盖") {
editText.text.toString().also { jsonString ->
binding.jsonRuleEdit.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> context.snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(dataJson = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]")
notifyRefresh(context)
it()
callback()
}
else -> context.snake(msg = "请输入有效内容")
}
@@ -169,23 +201,22 @@ object IconRuleManagerTool {
* @param context 实例
* @param sourceType 同步地址类型 - 默认自动获取已存储的键值
* @param customUrl 自定义同步地址 - 默认自动获取已存储的键值
* @param it 成功后回调
* @param callback 成功后回调
*/
fun sync(
context: Context,
sourceType: Int = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY),
customUrl: String = context.modulePrefs.get(DataConst.SOURCE_SYNC_WAY_CUSTOM_URL),
it: () -> Unit
sourceType: Int = ConfigData.iconRuleSourceSyncType,
customUrl: String = ConfigData.iconRuleSourceSyncCustomUrl,
callback: () -> Unit
) {
when (sourceType) {
TYPE_SOURCE_SYNC_WAY_1 ->
onRefreshing(context, url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main", it)
TYPE_SOURCE_SYNC_WAY_2 ->
onRefreshing(context, url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main", it)
TYPE_SOURCE_SYNC_WAY_3 ->
IconRuleSourceSyncType.GITHUB_RAW_PROXY_1 -> onRefreshing(context, SYNC_PROXY_1_URL, callback)
IconRuleSourceSyncType.GITHUB_RAW_PROXY_2 -> onRefreshing(context, SYNC_PROXY_2_URL, callback)
IconRuleSourceSyncType.GITHUB_RAW_DIRECT -> onRefreshing(context, SYNC_DIRECT_URL, callback)
IconRuleSourceSyncType.CUSTOM_URL ->
if (customUrl.isNotBlank())
if (customUrl.startsWith("http://") || customUrl.startsWith("https://"))
onRefreshingCustom(context, customUrl, it)
onRefreshingCustom(context, customUrl, callback)
else context.snakeOrNotify(title = "同步失败", msg = "同步地址不是一个合法的 URL")
else context.snakeOrNotify(title = "同步失败", msg = "同步地址不能为空")
else -> context.snakeOrNotify(title = "同步异常", msg = "同步类型错误")
@@ -196,14 +227,14 @@ object IconRuleManagerTool {
* 开始更新数据
* @param context 实例
* @param url
* @param it 成功后回调
* @param callback 成功后回调
*/
private fun onRefreshing(context: Context, url: String, it: () -> Unit) = checkingInternetConnect(context) {
fun doParse(callback: (Boolean) -> Unit = {}) {
private fun onRefreshing(context: Context, url: String, callback: () -> Unit) = checkingInternetConnect(context) {
fun doParse(result: (Boolean) -> Unit = {}) {
wait(context, url = "$url/OS/$OS_TAG/NotifyIconsSupportConfig.json") { isDone1, ctOS ->
callback(true)
result(true)
wait(context, url = "$url/APP/NotifyIconsSupportConfig.json") { isDone2, ctAPP ->
callback(false)
result(false)
IconPackParams(context).also { params ->
when {
isDone1 && isDone2 -> params.splicingJsonArray(ctOS, ctAPP).also {
@@ -214,18 +245,17 @@ object IconRuleManagerTool {
context.snakeOrNotify(title = "同步错误", msg = "目标地址不是有效的 JSON 数据")
params.isCompareDifferent(it) -> {
params.save(it)
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
notifyRefresh(context)
it()
callback()
}
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新") else Unit)
}
}
context is AppCompatActivity ->
context.showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n${if (isDone1.not()) ctOS else ctAPP}"
confirmButton(text = "再试一次") { syncByHand(context, it) }
confirmButton(text = "再试一次") { syncByHand(context, callback) }
cancelButton()
}
else -> pushNotify(context, title = "同步地址不可用", msg = if (isDone1.not()) ctOS else ctAPP)
@@ -248,12 +278,14 @@ object IconRuleManagerTool {
* 开始更新数据
* @param context 实例
* @param url
* @param it 成功后回调
* @param callback 成功后回调
*/
private fun onRefreshingCustom(context: Context, url: String, it: () -> Unit) = checkingInternetConnect(context) {
fun doParse(callback: () -> Unit = {}) {
private fun onRefreshingCustom(context: Context, url: String, callback: () -> Unit) = checkingInternetConnect(context) {
fun doParse(result: () -> Unit = {}) {
wait(context, url) { isDone, content ->
callback()
result()
/** 延迟重新回调防止对话框无法取消 */
delayedRun { result() }
IconPackParams(context).also { params ->
when {
isDone -> when {
@@ -263,11 +295,10 @@ object IconRuleManagerTool {
context.snakeOrNotify(title = "同步错误", msg = "目标地址不是有效的 JSON 数据")
params.isCompareDifferent(content) -> {
params.save(content)
pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
notifyRefresh(context)
it()
callback()
}
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新"))
else -> (if (context is AppCompatActivity) context.snake(msg = "列表数据已是最新") else Unit)
}
context is AppCompatActivity ->
context.showDialog {
@@ -293,16 +324,16 @@ object IconRuleManagerTool {
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
* @param callback 已连接回调
*/
private fun checkingInternetConnect(context: Context, it: () -> Unit) =
private fun checkingInternetConnect(context: Context, callback: () -> Unit) =
if (context is AppCompatActivity) context.showDialog {
title = "准备中"
progressContent = "正在检查网络连接情况"
noCancelable()
baseCheckingInternetConnect(context) { isDone ->
cancel()
if (isDone) it() else
if (isDone) callback() else
context.showDialog {
title = "网络不可用"
msg = "无法连接到互联网,请检查你当前的设备是否可以上网,且没有在手机管家中禁用本模块的联网权限。"
@@ -319,24 +350,24 @@ object IconRuleManagerTool {
}
}
} else baseCheckingInternetConnect(context) { isDone ->
if (isDone) it() else pushNotify(context, title = "网络不可用", msg = "无法连接到互联网,无法更新通知图标规则")
if (isDone) callback() else pushNotify(context, title = "网络不可用", msg = "络连接失败,无法更新通知图标规则,点击重试", isRetry = true)
}
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
* @param result 已连接回调
*/
private fun baseCheckingInternetConnect(context: Context, it: (Boolean) -> Unit) =
wait(context, url = "https://www.baidu.com") { isDone, _ -> it(isDone) }
private fun baseCheckingInternetConnect(context: Context, result: (Boolean) -> Unit) =
wait(context, url = "https://www.baidu.com") { isDone, _ -> result(isDone) }
/**
* 发送 GET 请求内容并等待
* @param context 实例
* @param url 请求地址
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
* @param result 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
*/
private fun wait(context: Context, url: String, it: (Boolean, String) -> Unit) = runCatching {
private fun wait(context: Context, url: String, result: (Boolean, String) -> Unit) = runCatching {
OkHttpClient().newBuilder().apply {
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
hostnameVerifier(SSLSocketClient.hostnameVerifier)
@@ -347,23 +378,23 @@ object IconRuleManagerTool {
.build()
).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
(context as? Activity?)?.runOnUiThread { it(false, e.toString()) } ?: it(false, e.toString())
(context as? Activity?)?.runOnUiThread { result(false, e.toString()) } ?: result(false, e.toString())
}
override fun onResponse(call: Call, response: Response) {
val bodyString = response.body?.string() ?: ""
(context as? Activity?)?.runOnUiThread { it(true, bodyString) } ?: it(true, bodyString)
val bodyString = response.body.string()
(context as? Activity?)?.runOnUiThread { result(true, bodyString) } ?: result(true, bodyString)
}
})
}.onFailure { it(false, "URL 无效") }
}.onFailure { result(false, "URL 无效") }
/**
* 推送通知图标更新通知
* @param context 实例
*/
private fun notifyRefresh(context: Context) {
if (context !is AppCompatActivity) return
SystemUITool.refreshSystemUI(context) { context.snake(msg = "通知图标优化名单已完成同步") }
if (context !is AppCompatActivity) pushNotify(context, title = "同步完成", msg = "已更新通知图标优化名单,点击查看")
SystemUITool.refreshSystemUI(context) { if (context is AppCompatActivity) context.snake(msg = "已更新通知图标优化名单") }
}
/**
@@ -372,8 +403,7 @@ object IconRuleManagerTool {
* @param msg 消息内容
*/
private fun Context.snakeOrNotify(title: String, msg: String) {
if (this !is AppCompatActivity)
pushNotify(context = this, title, msg)
if (this !is AppCompatActivity) pushNotify(context = this, title, msg)
else snake(msg)
}
@@ -382,8 +412,9 @@ object IconRuleManagerTool {
* @param context 实例 - 类型为 [AppCompatActivity] 时将不会推送通知
* @param title 通知标题
* @param msg 通知消息
* @param isRetry 是否发送重试动作 - 默认否
*/
private fun pushNotify(context: Context, title: String, msg: String) {
private fun pushNotify(context: Context, title: String, msg: String, isRetry: Boolean = false) {
if (context !is AppCompatActivity)
context.getSystemService<NotificationManager>()?.apply {
createNotificationChannel(
@@ -395,7 +426,7 @@ object IconRuleManagerTool {
notify(0, NotificationCompat.Builder(context, NOTIFY_CHANNEL).apply {
setContentTitle(title)
setContentText(msg)
color = OS_COLOR.toInt()
color = OS_COLOR
setAutoCancel(true)
setSmallIcon(R.drawable.ic_nf_icon_update)
setSound(null)
@@ -403,7 +434,9 @@ object IconRuleManagerTool {
setContentIntent(
PendingIntent.getActivity(
context, msg.hashCode(),
Intent(context, ConfigureActivity::class.java).apply { putExtra("isShowUpdDialog", false) },
Intent(context, ConfigureActivity::class.java).apply {
if (isRetry) putExtra("isDirectUpdate", true) else putExtra("isShowUpdDialog", false)
},
if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
)
)
@@ -444,11 +477,11 @@ object IconRuleManagerTool {
get() = object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
YLog.debug("TrustX509 --> $authType")
}
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
YLog.debug("TrustX509 --> $authType")
}
override fun getAcceptedIssuers() = arrayOf<X509Certificate>()

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,124 +18,208 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/2/8.
* This file is created by fankes on 2022/2/8.
*/
package com.fankes.coloros.notify.utils.tool
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.fankes.coloros.notify.const.Const
import com.fankes.coloros.notify.utils.factory.*
import android.os.Build
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.fankes.coloros.notify.const.PackageName
import com.fankes.coloros.notify.data.ConfigData
import com.fankes.coloros.notify.ui.activity.MainActivity
import com.fankes.coloros.notify.utils.factory.colorOSFullVersion
import com.fankes.coloros.notify.utils.factory.delayedRun
import com.fankes.coloros.notify.utils.factory.execShell
import com.fankes.coloros.notify.utils.factory.showDialog
import com.fankes.coloros.notify.utils.factory.snake
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.dataChannel
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.log.data.YLogData
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData
import java.util.Locale
/**
* 系统界面工具
*/
object SystemUITool {
/** 宿主广播回调 */
private var moduleHandlerCallback: ((Boolean, Boolean) -> Unit)? = null
private val CALL_HOST_REFRESH_CACHING = ChannelData("call_host_refresh_caching", false)
private val CALL_MODULE_REFRESH_RESULT = ChannelData("call_module_refresh_result", false)
/** 通知广播回调 */
private var remindHandlerCallback: ((Boolean, Boolean) -> Unit)? = null
/** 当前全部调试日志 */
private var debugLogs = listOf<YLogData>()
/** 当前启动器实例 */
private var launcher: ActivityResultLauncher<String>? = null
/**
* 注册广播
* @param context 实例
* 宿主注册监听
*/
fun register(context: Context) = runInSafe {
/** 注册广播检查模块激活状态 */
context.registerReceiver(moduleHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_MODULE_HANDLER_RECEIVER) })
/** 注册广播通知系统界面改变 */
context.registerReceiver(remindHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_REMIND_HANDLER_RECEIVER) })
}
object Host {
/**
* 取消注册广播
* @param context 实例
*/
fun unregister(context: Context) = runInSafe {
context.unregisterReceiver(moduleHandlerReceiver)
context.unregisterReceiver(remindHandlerReceiver)
/**
* 监听系统界面刷新改变
* @param param 实例
* @param result 回调 - ([Boolean] 是否成功)
*/
fun onRefreshSystemUI(param: PackageParam, result: (Boolean) -> Boolean) {
param.dataChannel.with { wait(CALL_HOST_REFRESH_CACHING) { put(CALL_MODULE_REFRESH_RESULT, result(it)) } }
}
}
/**
* 检查模块是否激活
* @param context 实例
* @param it 成功后回调 - ([Boolean] 是否激活,[Boolean] 是否有效)
* @param result 成功后回调
*/
fun checkingActivated(context: Context, it: (Boolean, Boolean) -> Unit) {
moduleHandlerCallback = it
context.sendBroadcast(Intent().apply {
action = Const.ACTION_MODULE_CHECKING_RECEIVER
putExtra(Const.MODULE_VERSION_VERIFY_TAG, Const.MODULE_VERSION_VERIFY)
})
fun checkingActivated(context: Context, result: (Boolean) -> Unit) =
context.dataChannel(PackageName.SYSTEMUI).checkingVersionEquals(result = result)
/**
* 注册导出调试日志启动器到 [AppCompatActivity]
* @param activity 实例
*/
fun registerExportDebugLogsLauncher(activity: AppCompatActivity) {
launcher = activity.registerForActivityResult(ActivityResultContracts.CreateDocument("*/application")) { result ->
runCatching {
result?.let { e ->
val content = "" +
"================================================================\n" +
" Generated by ColorOSNotifyIcon\n" +
" Project Url: https://github.com/fankes/ColorOSNotifyIcon\n" +
"================================================================\n\n" +
"[Device Brand]: ${Build.BRAND}\n" +
"[Device Model]: ${Build.MODEL}\n" +
"[Display]: ${Build.DISPLAY}\n" +
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
"[Android API Level]: ${Build.VERSION.SDK_INT}\n" +
"[ColorOS Version]: $colorOSFullVersion\n" +
"[System Locale]: ${Locale.getDefault()}\n\n" + YLog.contents(debugLogs).trim()
activity.contentResolver?.openOutputStream(e)?.apply { write(content.toByteArray()) }?.close()
activity.snake(msg = "导出完成")
} ?: activity.snake(msg = "已取消操作")
}.onFailure { activity.snake(msg = "导出过程发生错误") }
}
}
/**
* 获取并导出全部调试日志
* @param context 实例
*/
fun obtainAndExportDebugLogs(context: Context) {
/** 执行导出操作 */
fun doExport() {
context.dataChannel(PackageName.SYSTEMUI).obtainLoggerInMemoryData {
if (it.isNotEmpty()) {
debugLogs = it
runCatching { launcher?.launch("coloros_notification_icons_processing_logs.log") }
.onFailure { context.snake(msg = "启动系统文件选择器失败") }
} else context.snake(msg = "暂无调试日志")
}
}
if (YukiHookAPI.Status.isXposedModuleActive)
context.showDialog {
title = "导出全部调试日志"
msg = "调试日志中会包含当前系统推送的全部通知内容,其中可能包含你的个人隐私," +
"你可以在导出后的日志文件中选择将这些敏感信息模糊化处理再进行共享," +
"开发者使用并查看你导出的调试日志仅为排查与修复问题,并且在之后会及时销毁这些日志。\n\n" +
"继续导出即代表你已阅读并知悉上述内容。"
confirmButton(text = "继续") { doExport() }
cancelButton()
}
else context.snake(msg = "模块没有激活,请先激活模块")
}
/**
* 重启系统界面
* @param context 实例
*/
fun restartSystemUI(context: Context) =
fun restartSystemUI(context: Context) {
/** 动态刷新功能是否可用 */
val isDynamicAvailable = ConfigData.isEnableModule && MainActivity.isModuleRegular && MainActivity.isModuleValied
/** 当 Root 权限获取失败时显示对话框 */
fun showWhenAccessRootFail() =
context.showDialog {
title = "获取 Root 权限失败"
msg = "当前无法获取 Root 权限,请确认你的设备已经被 Root 且同意授予 Root 权限。\n" +
"如果你正在使用 Magisk 并安装了 Shamiko 模块," +
"请确认当前是否正处于白名单模式。 (白名单模式将导致无法申请 Root 权限)"
confirmButton(text = "我知道了")
}
context.showDialog {
title = "重启系统界面"
msg = "你确定要立即重启系统界面吗?\n\n" +
"重启过程会黑屏并等待进入锁屏重新解锁。"
"重启过程会黑屏并等待进入锁屏重新解锁。" + (if (isDynamicAvailable)
"\n\n你也可以选择“立即生效”来动态刷新系统界面并生效当前模块设置。" else "")
confirmButton {
execShell(cmd = "pgrep systemui").also { pid ->
if (pid.isNotBlank())
execShell(cmd = "kill -9 $pid")
else toast(msg = "ROOT 权限获取失败")
else showWhenAccessRootFail()
}
}
cancelButton()
if (isDynamicAvailable) neutralButton(text = "立即生效") { refreshSystemUI(context) }
}
}
/**
* 刷新系统界面状态栏与通知图标
* @param context 实例
* @param isRefreshCacheOnly 仅刷新缓存不刷新图标和通知改变 - 默认:否
* @param it 成功后回调
* @param callback 成功后回调
*/
fun refreshSystemUI(context: Context? = null, isRefreshCacheOnly: Boolean = false, it: () -> Unit = {}) = runInSafe {
fun sendMessage() {
(context ?: appContext).sendBroadcast(Intent().apply {
action = Const.ACTION_REMIND_CHECKING_RECEIVER
putExtra("isRefreshCacheOnly", isRefreshCacheOnly)
putExtra(Const.MODULE_VERSION_VERIFY_TAG, Const.MODULE_VERSION_VERIFY)
})
fun refreshSystemUI(context: Context? = null, isRefreshCacheOnly: Boolean = false, callback: () -> Unit = {}) {
/**
* 刷新系统界面
* @param result 回调结果
*/
fun doRefresh(result: (Boolean) -> Unit) {
context?.dataChannel(PackageName.SYSTEMUI)?.with {
wait(CALL_MODULE_REFRESH_RESULT) { result(it) }
put(CALL_HOST_REFRESH_CACHING, isRefreshCacheOnly)
}
}
if (isXposedModuleActive)
context?.showDialog {
title = "请稍后"
progressContent = "正在等待系统界面刷新"
/** 是否等待成功 */
var isWaited = false
/** 设置等待延迟 */
delayedRun(ms = 5000) {
if (isWaited) return@delayedRun
remindHandlerCallback = null
cancel()
context.snake(msg = "预计响应超时,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
}
remindHandlerCallback = { isGrasp, isValied ->
remindHandlerCallback = null
cancel()
isWaited = true
when {
isGrasp && !isValied ->
context.snake(msg = "请重启系统界面以生效模块更新", actionText = "立即重启") { restartSystemUI(context) }
else -> it()
when {
YukiHookAPI.Status.isXposedModuleActive && context is AppCompatActivity ->
context.showDialog {
title = "请稍后"
progressContent = "正在等待系统界面刷新"
/** 是否等待成功 */
var isWaited = false
/** 设置等待延迟 */
delayedRun(ms = 5000) {
if (isWaited) return@delayedRun
cancel()
context.snake(msg = "预计响应超时,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
}
checkingActivated(context) { isValied ->
when {
isValied.not() -> {
cancel()
isWaited = true
context.snake(msg = "请重启系统界面以生效模块更新", actionText = "立即重启") { restartSystemUI(context) }
}
else -> doRefresh {
cancel()
isWaited = true
callback()
if (it.not()) context.snake(msg = "刷新失败,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) }
}
}
}
noCancelable()
}
sendMessage()
noCancelable()
} ?: sendMessage()
else context?.snake(msg = "模块没有激活,更改不会生效")
YukiHookAPI.Status.isXposedModuleActive.not() && context is AppCompatActivity -> context.snake(msg = "模块没有激活,更改不会生效")
else -> doRefresh { callback() }
}
}
/**
@@ -143,29 +227,7 @@ object SystemUITool {
* @param context 实例
*/
fun showNeedRestartSnake(context: Context) =
if (isXposedModuleActive)
if (YukiHookAPI.Status.isXposedModuleActive)
context.snake(msg = "设置需要重启系统界面才能生效", actionText = "立即重启") { restartSystemUI(context) }
else context.snake(msg = "模块没有激活,更改不会生效")
/** 宿主广播接收器 */
private val moduleHandlerReceiver by lazy {
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val isRegular = intent?.getBooleanExtra("isRegular", false) ?: false
val isValied = intent?.getBooleanExtra("isValied", false) ?: false
moduleHandlerCallback?.invoke(isRegular, isValied)
}
}
}
/** 通知广播接收器 */
private val remindHandlerReceiver by lazy {
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val isGrasp = intent?.getBooleanExtra("isGrasp", false) ?: false
val isValied = intent?.getBooleanExtra("isValied", false) ?: false
remindHandlerCallback?.invoke(isGrasp, isValied)
}
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 20174 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* This software is non-free but opensource software: you can redistribute it
@@ -18,16 +18,20 @@
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/1/24.
* This file is created by fankes on 2023/9/17.
*/
package com.fankes.coloros.notify.hook
@file:Suppress("unused")
object HookConst {
package com.fankes.coloros.notify.wrapper
const val TYPE_SOURCE_SYNC_WAY_1 = 1000
const val TYPE_SOURCE_SYNC_WAY_2 = 2000
const val TYPE_SOURCE_SYNC_WAY_3 = 3000
import com.fankes.coloros.notify.BuildConfig
const val ANDROID_PACKAGE_NAME = "android"
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
/**
* [BuildConfig] 的包装
*/
object BuildConfigWrapper {
const val APPLICATION_ID = BuildConfig.APPLICATION_ID
const val VERSION_NAME = BuildConfig.VERSION_NAME
const val VERSION_CODE = BuildConfig.VERSION_CODE
val isDebug = BuildConfig.DEBUG
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150.1dp"
android:height="150dp"
android:viewportWidth="1025"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M983.8,314.4c-25.7,-60.8 -62.5,-115.4 -109.4,-162.3 -46.9,-46.9 -101.5,-83.7 -162.3,-109.4 -62.9,-26.7 -129.9,-40.2 -198.8,-40.2S377.5,16 314.5,42.7C253.8,68.4 199.1,105.2 152.3,152.2c-46.9,46.9 -83.7,101.5 -109.4,162.3 -26.7,62.9 -40.2,129.9 -40.2,198.8s13.5,135.8 40.2,198.8c25.7,60.8 62.5,115.4 109.4,162.3 46.9,46.9 101.5,83.7 162.3,109.4 62.9,26.7 129.9,40.2 198.8,40.2s135.8,-13.5 198.8,-40.2c60.8,-25.7 115.4,-62.5 162.3,-109.4 46.9,-46.9 83.7,-101.5 109.4,-162.3 26.7,-62.9 40.2,-129.9 40.2,-198.8s-13.6,-135.9 -40.3,-198.9zM550.5,768.2c0,21 -17,38 -38,38s-38,-17 -38,-38L474.5,395.6c0,-21 17,-38 38,-38s38,17 38,38v372.6zM510.8,305.5c-29.2,0 -52.7,-23.7 -52.7,-52.7 0,-29.2 23.7,-52.7 52.7,-52.7 29.2,0 52.7,23.7 52.7,52.7 0.1,29.2 -23.6,52.7 -52.7,52.7z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="192dp"
android:height="150.39165dp"
android:viewportWidth="1307"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M268.7,566.5h929.6c36.3,0 72.6,-29 72.6,-72.6 0,-36.3 -29,-72.6 -72.6,-72.6H305l297.8,-297.8c29,-29 29,-72.6 0,-101.7 -29,-29 -72.6,-29 -101.7,0L72.6,450.3c-14.5,14.5 -21.8,36.3 -21.8,58.1 0,21.8 0,43.6 21.8,58.1l428.5,428.5c29,29 72.6,29 101.7,0 29,-29 29,-72.6 0,-101.7l-334.1,-326.8z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="184.6dp"
android:height="150dp"
android:viewportWidth="1260"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M419.2,548.3L17,108.9C-23.6,64.4 13,0 79.7,0h846.5c66.6,0 103.3,64.4 62.8,108.9l-402.2,439.3v472.7c0,1.6 -1.3,2.9 -2.8,2.9a8.3,8.3 0,0 1,-5.2 -1.8l-154.3,-120a13.7,13.7 0,0 1,-5.3 -10.8L419.2,548.3zM838.2,511.9L1257.3,511.9L1257.3,597.2h-419.1L838.2,511.9zM838.2,682.5L1257.3,682.5v85.3h-419.1L838.2,682.5zM838.2,853.2L1257.3,853.2v85.3h-419.1v-85.3z" />
</vector>

View File

@@ -0,0 +1,52 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M11,16V42"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M24,29V42"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M24,19V6"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M37,6V32"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M11,16C13.761,16 16,13.761 16,11C16,8.239 13.761,6 11,6C8.239,6 6,8.239 6,11C6,13.761 8.239,16 11,16Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M24,29C26.761,29 29,26.761 29,24C29,21.239 26.761,19 24,19C21.239,19 19,21.239 19,24C19,26.761 21.239,29 24,29Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M37,42C39.761,42 42,39.761 42,37C42,34.239 39.761,32 37,32C34.239,32 32,34.239 32,37C32,39.761 34.239,42 37,42Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M512,12.6c-282.8,0 -512,229.2 -512,512 0,226.2 146.7,418.1 350.1,485.8 25.6,4.7 35,-11.1 35,-24.6 0,-12.2 -0.5,-52.5 -0.7,-95.3 -142.5,31 -172.5,-60.4 -172.5,-60.4 -23.3,-59.2 -56.8,-74.9 -56.8,-74.9 -46.5,-31.8 3.5,-31.1 3.5,-31.1 51.4,3.6 78.5,52.8 78.5,52.8 45.7,78.3 119.8,55.6 149,42.6 4.6,-33.1 17.9,-55.7 32.5,-68.5 -113.7,-12.9 -233.3,-56.9 -233.3,-253 0,-55.9 20,-101.6 52.8,-137.4 -5.3,-12.9 -22.8,-65 5,-135.5 0,0 43,-13.8 140.8,52.5 40.8,-11.4 84.6,-17 128.2,-17.2 43.5,0.2 87.3,5.9 128.3,17.2 97.7,-66.2 140.6,-52.5 140.6,-52.5 27.9,70.5 10.3,122.6 5,135.5 32.8,35.8 52.7,81.5 52.7,137.4 0,196.6 -119.8,239.9 -233.8,252.6 18.4,15.9 34.7,47 34.7,94.8 0,68.5 -0.6,123.6 -0.6,140.5 0,13.6 9.2,29.6 35.2,24.6 203.3,-67.8 349.9,-259.6 349.9,-485.8 0,-282.8 -229.2,-512 -512,-512z" />
</vector>

View File

@@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M9,18V42H39V18L24,6L9,18Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M19,29V42H29V29H19Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M9,42H39"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round" />
</vector>

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M24,44C29.523,44 34.523,41.761 38.142,38.142C41.761,34.523 44,29.523 44,24C44,18.477 41.761,13.477 38.142,9.858C34.523,6.239 29.523,4 24,4C18.477,4 13.477,6.239 9.858,9.858C6.239,13.477 4,18.477 4,24C4,29.523 6.239,34.523 9.858,38.142C13.477,41.761 18.477,44 24,44Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:pathData="M24,11C25.381,11 26.5,12.119 26.5,13.5C26.5,14.881 25.381,16 24,16C22.619,16 21.5,14.881 21.5,13.5C21.5,12.119 22.619,11 24,11Z" />
<path
android:fillColor="#00000000"
android:pathData="M24.5,34V20H23.5H22.5"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M21,34H28"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="m522.8,93.9c-244.2,0 -442.1,173.2 -442.1,386.9 0,122.1 64.9,230.9 165.8,301.7v195.7l193.7,-117.5c26.8,4.4 54.3,7 82.6,7 244.2,0 442.1,-173.2 442.1,-386.9C965,267.1 767.1,93.9 522.8,93.9Z"
android:strokeWidth="1.10883" />
</vector>

View File

@@ -0,0 +1,36 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M20.071,9.586L15.828,5.343C15.047,4.562 13.781,4.562 13,5.343L7.343,11C6.562,11.781 6.562,13.047 7.343,13.828L11.586,18.071"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M28.929,37.414L33.171,41.657C33.952,42.438 35.219,42.438 36,41.657L41.657,36C42.438,35.219 42.438,33.953 41.657,33.172L37.414,28.929"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M36.021,6.322L41.677,11.979A2,2 0,0 1,41.677 14.808L14.807,41.678A2,2 0,0 1,11.979 41.678L6.322,36.021A2,2 132.403,0 1,6.322 33.192L33.192,6.322A2,2 102.008,0 1,36.021 6.322z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:pathData="M24,24m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
<path
android:fillColor="#ffffff"
android:pathData="M20,28m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
<path
android:fillColor="#ffffff"
android:pathData="M28,20m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
</vector>

View File

@@ -4,8 +4,7 @@
android:tint="@color/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:fillColor="#ffffffff"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z" />
</vector>
</vector>

View File

@@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M10,38V18C10,10.268 16.268,4 24,4C31.732,4 38,10.268 38,18V38M4,38H44"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M24,44C26.761,44 29,41.761 29,39V38H19V39C19,41.761 21.239,44 24,44Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</vector>

View File

@@ -3,8 +3,7 @@
android:height="150dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/white"
android:fillColor="#ffffffff"
android:pathData="M192,736 L192,416C192,261.2 302,132.1 448,102.4L448,64C448,28.6 476.6,0 512,0 547.4,0 576,28.6 576,64L576,102.4C722,132.1 832,261.2 832,416L832,736 864.1,736C899.4,736 928,764.4 928,800 928,835.4 899.4,864 864.1,864L159.9,864C124.6,864 96,835.6 96,800 96,764.6 124.6,736 159.9,736L192,736ZM608,928C608,981 565,1024 512,1024 459,1024 416,981 416,928L608,928Z" />
</vector>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="50dp"
android:height="50dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M906.4,791.6a247.9,247.9 0,0 0,-98.1 -186.5L808.3,399.4c0,-118.8 -85.1,-218.9 -197.4,-242.5v-4.7c0,-54.2 -44.5,-98.6 -98.8,-98.6 -54.3,0 -98.8,44.4 -98.8,98.6v4.7C300.8,180.5 215.7,280.4 215.7,399.4v205.8a247.7,247.7 0,0 0,-98.1 186.5h98.1v0.6h592.5v-0.6h98.1v-0.1zM491.1,970.4h24.5c64.3,0 117.8,-48.5 125.6,-110.7L383,859.8a126.8,126.8 0,0 0,125.6 110.7h-17.5z" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="m563.5,143.3m-41,0v0q-41,0 -41,41v573.4q0,41 41,41v0q41,0 41,-41v-573.4q0,-41 -41,-41z" />
<path
android:fillColor="#ffffff"
android:pathData="M521.5,796.2 L318.2,593a41,41 0,1 0,-57.9 57.9l231.7,231.7a41,41 0,0 0,58.4 0.5L782.2,651.4a41,41 0,1 0,-57.9 -57.9z" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="m563.5,895.1m-41,0v0q-41,0 -41,-41v-573.4q0,-41 41,-41v0q41,0 41,41v573.4q0,41 -41,41z" />
<path
android:fillColor="#ffffff"
android:pathData="M521.5,242.2 L318.2,445.4a41,41 0,1 1,-57.9 -57.9l231.7,-231.7a41,41 0,0 1,58.4 -0.5l231.7,231.7a41,41 0,1 1,-57.9 57.9z" />
</vector>

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M44,16C44,22.627 38.627,28 32,28C29.973,28 28.064,27.497 26.39,26.61L9,44L4,39L21.39,21.61C20.503,19.936 20,18.027 20,16C20,9.373 25.373,4 32,4C34.027,4 35.936,4.502 37.61,5.39L30,13L35,18L42.61,10.39C43.498,12.064 44,13.973 44,16Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</vector>

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="4800"
android:viewportHeight="4800">
<path
android:fillColor="#ffffff"
android:pathData="M2270.9,56.1L2527.1,56.1A200,307.5 0,0 1,2727.1 363.6L2727.1,1098.6A200,307.5 0,0 1,2527.1 1406.1L2270.9,1406.1A200,307.5 0,0 1,2070.9 1098.6L2070.9,363.6A200,307.5 0,0 1,2270.9 56.1z" />
<path
android:fillColor="#ffffff"
android:pathData="M2270.9,3406.1L2527.1,3406.1A200,307.5 0,0 1,2727.1 3713.6L2727.1,4448.6A200,307.5 0,0 1,2527.1 4756.1L2270.9,4756.1A200,307.5 0,0 1,2070.9 4448.6L2070.9,3713.6A200,307.5 0,0 1,2270.9 3406.1z" />
<path
android:fillColor="#ffffff"
android:pathData="M646.7,835L827.8,653.9A200,307.5 135,0 1,1186.7 729.9L1706.4,1249.6A200,307.5 135,0 1,1782.4 1608.5L1601.3,1789.6A307.5,200 45,0 1,1242.4 1713.6L722.7,1193.9A307.5,200 45,0 1,646.7 835z" />
<path
android:fillColor="#ffffff"
android:pathData="M3015.5,3203.8L3196.7,3022.7A200,307.5 135,0 1,3555.5 3098.7L4075.2,3618.4A307.5,200 45,0 1,4151.3 3977.3L3970.1,4158.4A307.5,200 45,0 1,3611.3 4082.4L3091.5,3562.7A307.5,200 45,0 1,3015.5 3203.8z" />
<path
android:fillColor="#ffffff"
android:pathData="M49,2534.2L49,2278A200,307.5 90,0 1,356.5 2078L1091.5,2078A200,307.5 90,0 1,1399 2278L1399,2534.2A200,307.5 90,0 1,1091.5 2734.2L356.5,2734.2A200,307.5 90,0 1,49 2534.2z" />
<path
android:fillColor="#ffffff"
android:pathData="M3399,2534.2L3399,2278.1A200,307.5 90,0 1,3706.5 2078.1L4441.5,2078.1A200,307.5 90,0 1,4749 2278.1L4749,2534.2A200,307.5 90,0 1,4441.5 2734.2L3706.5,2734.2A200,307.5 90,0 1,3399 2534.2z" />
<path
android:fillColor="#ffffff"
android:pathData="M827.8,4158.4L646.7,3977.3A307.5,200 135,0 1,722.7 3618.4L1242.4,3098.7A200,307.5 45,0 1,1601.3 3022.7L1782.4,3203.8A200,307.5 45,0 1,1706.4 3562.7L1186.7,4082.4A200,307.5 45,0 1,827.8 4158.4z" />
<path
android:fillColor="#ffffff"
android:pathData="M3196.7,1789.6L3015.5,1608.4A307.5,200 135,0 1,3091.5 1249.6L3611.3,729.9A307.5,200 135,0 1,3970.1 653.9L4151.3,835A200,307.5 45,0 1,4075.2 1193.9L3555.5,1713.6A200,307.5 45,0 1,3196.7 1789.6z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1008.7"
android:viewportHeight="1008.7">
<path
android:fillColor="#ffffff"
android:pathData="M504.4,0C226.3,0 0,226.3 0,504.4 0,782.5 226.3,1008.7 504.4,1008.7c278.1,0 504.4,-226.3 504.4,-504.4C1008.7,226.3 782.5,0 504.4,0ZM786.6,407.7 L458.6,743.9c-7.8,8 -18.6,12.6 -29.8,12.6h-0.2c-11.1,0 -21.8,-4.4 -29.7,-12.3L222.5,567.9c-16.4,-16.4 -16.4,-43 0,-59.4 16.4,-16.4 43,-16.4 59.4,0L428.2,654.8 726.5,348.9c16.3,-16.6 42.9,-16.9 59.4,-0.7 16.6,16.2 16.9,42.8 0.7,59.4z" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M628.5,544a224,224 0,0 0,-96 116.5l60.2,21.8a160,160 0,0 1,68.8 -83.2,152.6 152.6,0 0,1 117.4,-16 154.9,154.9 0,0 1,92.5 69.8l-39.4,28.8 116.2,32 11.8,-121.6 -35.8,25.3A217.9,217.9 0,0 0,628.5 544zM810.9,875.5a150.4,150.4 0,0 1,-118.1 9.6,155.8 155.8,0 0,1 -88.6,-74.6l40,-25.3 -114.2,-39 -17.9,121.6 37.1,-23.4A217.9,217.9 0,0 0,672 946.2a224,224 0,0 0,67.8 10.9,215 215,0 0,0 99.5,-24.6 221.1,221.1 0,0 0,111.4 -136l-61.4,-17.9a155.2,155.2 0,0 1,-78.4 97z" />
<path
android:fillColor="#ffffff"
android:pathData="M736,448a285.1,285.1 0,0 1,138.6 35.5L874.6,119a48,48 0,0 0,-48 -48h-640a48,48 0,0 0,-48 48v768a48,48 0,0 0,48 48h341.8A288,288 0,0 1,736 448zM314.6,311.4h320v64h-320zM314.6,439.4h192v64h-192z" />
</vector>

View File

@@ -4,8 +4,7 @@
android:tint="@color/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:fillColor="#ffffffff"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM15.29,16.71L11,12.41V7h2v4.59l3.71,3.71L15.29,16.71z" />
</vector>
</vector>

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:pathData="M37,37C39.209,37 41,35.209 41,33C41,31.527 39.667,29.527 37,27C34.333,29.527 33,31.527 33,33C33,35.209 34.791,37 37,37Z" />
<path
android:fillColor="#00000000"
android:pathData="M20.854,5.504L24.389,9.04"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:pathData="M23.682,8.333L8.125,23.889L19.439,35.203L34.995,19.646L23.682,8.333Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M12,20.073L28.961,25.65"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:pathData="M4,43H44"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="52dp"
android:height="52dp"
android:viewportWidth="739.56"
android:viewportHeight="739.56">
<path
android:fillColor="#ffffff"
android:pathData="m207.74,575.04c-105.24,0 -192,-86.76 -192,-192 0,-105.24 86.76,-192 192,-192 36.98,0 72.53,9.96 103.82,29.87 12.8,8.53 17.07,25.6 8.53,39.82 -8.53,12.8 -25.6,17.07 -39.82,8.53 -21.33,-14.22 -46.93,-21.33 -72.53,-21.33 -73.96,0 -135.11,61.16 -135.11,135.11 0,73.96 61.16,135.11 135.11,135.11 73.96,0 135.11,-61.16 135.11,-135.11 0,-15.64 12.8,-28.44 28.44,-28.44 15.64,0 28.44,12.8 28.44,28.44 0,105.24 -86.76,192 -192,192z" />
<path
android:fillColor="#ffffff"
android:pathData="m534.85,575.04c-39.82,0 -78.22,-11.38 -109.51,-34.13 -12.8,-8.53 -15.64,-27.02 -7.11,-39.82 8.53,-12.8 27.02,-15.64 39.82,-7.11 22.76,15.64 49.78,24.18 76.8,24.18 73.96,0 135.11,-61.16 135.11,-135.11 0,-73.96 -61.16,-135.11 -135.11,-135.11 -73.96,0 -135.11,61.16 -135.11,135.11 0,15.64 -12.8,28.44 -28.44,28.44 -15.64,0 -28.44,-12.8 -28.44,-28.44 0,-105.24 86.76,-192 192,-192 105.24,0 192,85.33 192,192 0,105.24 -86.76,192 -192,192z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="m512,794a44.8,44.8 0,1 1,44.8 -44.8,44.8 44.8,0 0,1 -44.8,44.8zM471.9,230.8a40.1,40.1 0,0 1,80.2 0v369.1a40.1,40.1 0,0 1,-79.8 0zM512,0A512,512 0,1 0,1024 512,512 512,0 0,0 512,0Z" />
</vector>

View File

@@ -25,7 +25,7 @@
android:layout_height="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:src="@mipmap/ic_back"
android:src="@drawable/ic_back"
android:tint="@color/colorTextGray"
android:tooltipText="返回" />
@@ -59,19 +59,18 @@
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/config_title_up"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_page_top"
android:layout_width="27dp"
android:layout_height="27dp"
android:src="@drawable/ic_page_top"
android:tint="@color/colorTextGray"
android:tooltipText="滚动到顶部" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/config_title_down"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="15dp"
android:src="@mipmap/ic_page_bottom"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_marginEnd="13dp"
android:src="@drawable/ic_page_bottom"
android:tint="@color/colorTextGray"
android:tooltipText="滚动到底部" />
@@ -80,7 +79,7 @@
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_filter"
android:src="@drawable/ic_filter"
android:tint="@color/colorTextGray"
android:tooltipText="按条件过滤" />
@@ -89,7 +88,7 @@
android:layout_width="23dp"
android:layout_height="23dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_sync"
android:src="@drawable/ic_sync"
android:tint="@color/colorTextGray"
android:tooltipText="同步列表" />
</LinearLayout>
@@ -111,7 +110,7 @@
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:alpha="0.85"
android:src="@mipmap/ic_about"
android:src="@drawable/ic_about"
android:tint="@color/colorTextDark" />
<TextView

View File

@@ -32,22 +32,22 @@
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_restart_icon"
style="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_marginEnd="15dp"
android:alpha="0.85"
android:src="@mipmap/ic_restart"
android:src="@drawable/ic_restart"
android:tint="@color/colorTextGray"
android:tooltipText="重启系统界面" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_github_icon"
style="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginEnd="5dp"
android:alpha="0.85"
android:src="@mipmap/ic_github"
android:src="@drawable/ic_github"
android:tint="@color/colorTextGray"
android:tooltipText="项目地址" />
</LinearLayout>
@@ -70,7 +70,7 @@
android:layout_height="25dp"
android:layout_marginStart="25dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_warn"
android:src="@drawable/ic_warn"
android:tint="@color/white" />
<LinearLayout
@@ -186,7 +186,7 @@
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:alpha="0.85"
android:src="@mipmap/ic_about"
android:src="@drawable/ic_about"
android:tint="@color/colorTextDark" />
<TextView
@@ -211,20 +211,30 @@
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:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center|start">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_basic" />
app:cardBackgroundColor="#009688"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_preference" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -236,33 +246,74 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.view.MaterialSwitch
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/module_enable_switch"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:text="启用模块"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<com.fankes.coloros.notify.ui.view.MaterialSwitch
android:id="@+id/module_enable_log_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="5dp"
android:text="启用调试日志"
android:text="启用模块"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="模块关闭后一切功能都将彻底停止工作。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<LinearLayout
android:id="@+id/module_enable_log_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="vertical">
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/module_enable_log_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:text="启用调试日志"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/exp_all_debug_log_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="13dp"
android:layout_marginRight="13dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_button_round"
android:gravity="center"
android:padding="10dp"
android:singleLine="true"
android:text="导出全部调试日志"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<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:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
@@ -286,11 +337,21 @@
android:layout_height="wrap_content"
android:gravity="center|start">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_control" />
app:cardBackgroundColor="#2196F3"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_function" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -302,7 +363,7 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.view.MaterialSwitch
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/dev_notify_config_switch"
android:layout_width="match_parent"
android:layout_height="40dp"
@@ -321,7 +382,7 @@
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.coloros.notify.ui.view.MaterialSwitch
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/crcp_notify_config_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
@@ -340,7 +401,7 @@
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.coloros.notify.ui.view.MaterialSwitch
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/dnd_notify_config_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
@@ -361,7 +422,72 @@
</LinearLayout>
<LinearLayout
android:id="@+id/a12_style_config_item"
android:id="@+id/color_icon_hook_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center|start">
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
app:cardBackgroundColor="#66BB6A"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_modify" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="图标调整"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/color_icon_compat_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启用兼容模式"
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="如果发现通知图标颜色判定不正常可启用兼容模式,一般情况下不建议启用兼容模式,发生问题请关闭兼容模式。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/notify_style_config_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
@@ -381,11 +507,21 @@
android:paddingLeft="15dp"
android:paddingRight="15dp">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_nf_style" />
app:cardBackgroundColor="#BA68C8"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_theme" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -397,14 +533,14 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.view.MaterialSwitch
android:id="@+id/a12_style_config_switch"
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/md3_style_config_switch"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="启用 Android 12 通知风格"
android:text="启用 Material 3 通知图标风格"
android:textAllCaps="false"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
@@ -417,69 +553,160 @@
android:lineSpacingExtra="6dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="无论任何版本,开启后都将使用 Android 12 原生通知图标方式为通知栏推送的通知图标增加外部圆形轮廓。\n目标为 Android 12 的系统将自动使用系统主题色着色。"
android:text="无论任何版本,开启后都将使用 Android 12 开始的 Material 3 通知图标设计方式为通知栏的通知图标增加外部轮廓。\n目标为 Android 12 及以上的系统将自动使用系统主题色着色。"
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="255"
android:min="0" />
<LinearLayout
android:id="@+id/notify_panel_config_text_panel"
android:id="@+id/notify_icon_custom_corner_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:paddingTop="5dp"
android:paddingBottom="15dp">
android:layout_marginBottom="10dp"
android:orientation="vertical">
<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: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="13.5sp" />
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_marginLeft="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:max="10"
android:min="0"
android:progress="10" />
<LinearLayout
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_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:id="@+id/notify_panel_config_text"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center"
android:maxWidth="100dp"
android:singleLine="true"
android:text="%1"
android:textColor="@color/colorTextGray"
android:textSize="15sp"
android:textStyle="bold" />
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="你可以拖拽滑动条来调整通知栏中的图标边框圆角大小。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/notify_icon_force_system_color_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/notify_icon_force_system_color_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginBottom="5dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="通知栏中的图标使用系统默认着色"
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:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="此选项默认关闭,开启后下拉通知栏中的通知图标将忽略图标自身的着色属性,全部使用系统默认颜色 (系统提供的统一色调) 着色,这是一个破坏原生通知图标的行为,仅针对部分有需要的用户而添加,我们不推荐开启这个功能,请根据个人偏好进行选择是否需要开启。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="vertical">
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/notify_icon_force_app_icon_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginBottom="5dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="通知栏中的图标强制为 APP 图标"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="此选项默认关闭,开启后下拉通知栏中的通知图标将会被替换为 APP 自身图标,但是不会更改状态栏中的通知图标,这是一个破坏原生通知图标的行为,仅针对部分有需要的用户而添加,我们不推荐开启这个功能,请根据个人偏好进行选择是否需要开启。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/notify_media_panel_auto_exp_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginBottom="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"
@@ -487,21 +714,120 @@
android:lineSpacingExtra="6dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="开启自定义功能后你可以拖拽滑动条来调整通知面板的透明度0 为全透明255 为不透明。"
android:text="ColorOS 的媒体通知在播放的时候默认是折叠状态,开启后只要推送媒体通知以及恢复播放状态都会自动展开通知。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
<LinearLayout
android:id="@+id/notify_panel_config_item"
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 测试通过,不能保证可以在任何系统版本中生效,以及和各种通知栏主题模块配合使用,如有冲突请关闭此功能或禁用相关重复功能的主题模块。\n已知问题悬浮通知也会被透明在透明度较低的时候下拉会有阴影光晕后期看需求再解决。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
android:orientation="vertical">
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/notify_panel_config_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginBottom="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、13 测试通过,不能保证可以在任何系统版本中生效,以及和各种通知栏主题模块配合使用,如有冲突请关闭此功能或禁用相关重复功能的主题模块。"
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) 被折叠的通知自动消除 (但并没有完全消除) 后,可能存在通知条目背景全透明的问题,此问题属于系统 BUG目前暂无有效的解决办法来判断子布局是否已从叠加的通知条目中被移出。\n(3) 媒体通知可能在低于 ColorOS 12.1 的系统上无法生效透明度,旧版本不再适配,该问题不会被修复。"
android:textColor="@color/colorTextDark"
android:textSize="12sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
@@ -516,22 +842,30 @@
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
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_notify" />
app:cardBackgroundColor="#FF7043"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_notify" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -543,12 +877,12 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.view.MaterialSwitch
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
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" />
@@ -557,8 +891,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"
@@ -571,8 +905,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"
@@ -583,24 +917,52 @@
<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"
android:text="此选项默认开启,开启后将对优化名单内的 APP 通知小图标使用单色调进行修复,特别是通过系统级别推送的通知,它们始终是 APP 默认图标(彩色的 APP 图标),修复后使得它们的图标看起来更加符合 Android 原生的统一规范。"
android:text="此选项默认开启,开启后将对优化名单内的 APP 通知小图标使用单色调进行修复,特别是通过系统级别推送的通知,它们始终是 APP 默认图标 (彩色的 APP 图标),修复后使得它们的图标看起来更加符合 Android 原生的统一规范。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<LinearLayout
android:id="@+id/notify_icon_fix_placeholder_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.widget.MaterialSwitch
android:id="@+id/notify_icon_fix_placeholder_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginBottom="5dp"
android:text="使用占位符修补未适配的通知图标"
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="此选项默认关闭,当发现未适配的彩色通知图标时,状态栏中显示的通知图标将会使用预置的占位符图标进行修补,通知栏中显示的通知图标保持原始图标不变。"
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
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/notify_icon_fix_notify_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
@@ -624,12 +986,12 @@
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">
<com.fankes.coloros.notify.ui.view.MaterialSwitch
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/notify_icon_auto_sync_switch"
android:layout_width="match_parent"
android:layout_height="30dp"
@@ -725,11 +1087,21 @@
android:layout_height="wrap_content"
android:gravity="center|start">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_home" />
app:cardBackgroundColor="#FFFF9800"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_home" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -741,7 +1113,7 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.view.MaterialSwitch
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/hide_icon_in_launcher_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -789,11 +1161,21 @@
android:layout_marginBottom="15dp"
android:gravity="center|start">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_help" />
app:cardBackgroundColor="#FF03A9F4"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2dp"
android:src="@drawable/ic_info" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -831,7 +1213,7 @@
android:layout_marginBottom="10dp"
android:alpha="0.8"
android:lineSpacingExtra="10dp"
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) 由于机型有限,仅对 ColorOS 12~12.1 测试正常运行,根据酷友的需要云调试修复 ColorOS 11 的问题,其它版本情况未知,请自行进行测试。\n(2) OxygenOS 只支持 12 版本,其它类 ColorOS 魔改的 UI 理论没有问题,请自行测试。\n(3) 动态刷新系统界面为实验性功能,若动态地改变了系统深色模式,调节了分辨率和字体大小,状态栏的通知图标会还原,或在刷新时发生图标黑白块问题,发生类似这种情况请手动重启一次系统界面即可解决。"
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) 由于机型有限,仅对 ColorOS 1212.1、13 测试正常运行,根据酷友的需要云调试修复 ColorOS 11 的问题,其它版本情况未知,请自行进行测试。\n(2) OxygenOS 只支持 12、12.1、13 版本,其它类 ColorOS 魔改的 UI 理论没有问题,请自行测试。\n(3) 动态刷新系统界面为实验性功能,若动态地改变了系统深色模式,调节了分辨率和字体大小,状态栏、通知栏中的通知图标会还原或直接不起作用,或在刷新时发生图标黑白块问题,发生类似这种情况请手动重启一次系统界面即可解决。\n(4) 部分机型的 ColorOS 12、12.1 版本概率性出现通知栏的通知图标黑白块问题,属于系统强制重新设置图标问题,无法修复,建议可以更新到 ColorOS 13 的机型更新系统解决,不再负责修复这个问题。\n(5) 由于 ColorOS 15 版本的系统性通知图标行为变更,系统强制在通知图标初始化阶段就将图标强制替换为 APP 彩色图标进行破坏,所以目前加入了 “系统框架” 作用域如果在模块安装后没有自动勾选此作用域请手动进行勾选并重新启动系统以修复此破坏行为ColorOS 15 以下版本的系统无需勾选。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
@@ -881,7 +1263,8 @@
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/bg_qr_pay" />
android:adjustViewBounds="true"
android:src="@mipmap/bg_payment_code" />
</androidx.cardview.widget.CardView>
<TextView
@@ -919,7 +1302,36 @@
android:ellipsize="end"
android:lineSpacingExtra="6dp"
android:maxLines="2"
android:text="此模块使用 YukiHookAPI 构建。\n了解更多 https://github.com/fankes/YukiHookAPI"
android:text="此模块使用 YukiHookAPI 构建。\n了解更多 https://github.com/HighCapable/YukiHookAPI"
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_permotion_round"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_kavaref" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:ellipsize="end"
android:lineSpacingExtra="6dp"
android:maxLines="2"
android:text="此模块使用 KavaRef 强力驱动。\n了解更多 https://github.com/HighCapable/KavaRef"
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More