├── .gitignore
├── Functions-log.md
├── LICENSE
├── LOG-CN.md
├── LOG-EN.md
├── README-CN.md
├── README.md
├── app
├── .gitignore
├── and-res-guard.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ └── xposed_init
│ ├── ic_launcher-web.png
│ ├── java
│ └── com
│ │ └── tianma
│ │ └── tweaks
│ │ └── miui
│ │ ├── app
│ │ ├── App.kt
│ │ ├── MainActivity.kt
│ │ ├── base
│ │ │ ├── BaseActivity.kt
│ │ │ └── BasePreferenceFragment.kt
│ │ ├── fragment
│ │ │ ├── BaseSettingsFragment.kt
│ │ │ ├── DropDownStatusBarSettingsFragment.kt
│ │ │ ├── GeneralSettingsFragment.kt
│ │ │ ├── KeyguardSettingsFragment.kt
│ │ │ ├── SettingsFragmentPagerAdapter.kt
│ │ │ └── StatusBarSettingsFragment.kt
│ │ └── widget
│ │ │ ├── dialog
│ │ │ └── OneSentenceSettingsDialogWrapper.kt
│ │ │ └── tag
│ │ │ ├── ItemClickCallback.kt
│ │ │ ├── TagAdapter.kt
│ │ │ └── TagBean.java
│ │ ├── cons
│ │ ├── AppConst.kt
│ │ └── PrefConst.kt
│ │ ├── data
│ │ ├── http
│ │ │ ├── APIConst.kt
│ │ │ ├── entity
│ │ │ │ ├── Hitokoto.kt
│ │ │ │ └── Poem.kt
│ │ │ ├── repository
│ │ │ │ └── DataRepository.kt
│ │ │ └── service
│ │ │ │ ├── HitokotoService.kt
│ │ │ │ ├── PoemService.kt
│ │ │ │ └── ServiceGenerator.kt
│ │ └── sp
│ │ │ ├── PreferenceContainer.kt
│ │ │ └── XPrefContainer.kt
│ │ ├── utils
│ │ ├── ContextUtils.kt
│ │ ├── DebugHelper.kt
│ │ ├── ModuleUtils.kt
│ │ ├── PackageUtils.kt
│ │ ├── PreferencesUtils.kt
│ │ ├── ReflectionExt.kt
│ │ ├── ReflectionUtils.java
│ │ ├── ResolutionUtils.kt
│ │ ├── RootUtils.kt
│ │ ├── SPUtils.kt
│ │ ├── StorageUtils.kt
│ │ ├── Utils.kt
│ │ ├── XLog.kt
│ │ ├── XSPUtils.java
│ │ ├── prefs
│ │ │ ├── PreferenceDelegate.kt
│ │ │ └── XPreferenceDelegate.kt
│ │ └── rom
│ │ │ ├── MiuiUtils.java
│ │ │ ├── MiuiVersion.java
│ │ │ └── RomUtils.java
│ │ └── xp
│ │ ├── HookEntry.kt
│ │ ├── hook
│ │ ├── BaseHook.kt
│ │ ├── BaseSubHook.kt
│ │ ├── IHook.kt
│ │ ├── launcher
│ │ │ ├── MiuiLauncherHook.kt
│ │ │ └── WorkSpaceHook.kt
│ │ ├── self
│ │ │ └── ModuleUtilsHook.kt
│ │ └── systemui
│ │ │ ├── SystemUIHook.java
│ │ │ ├── helper
│ │ │ └── ResHelpers.java
│ │ │ ├── hitokoto
│ │ │ └── OneSentenceManager.kt
│ │ │ ├── keyguard
│ │ │ ├── def
│ │ │ │ ├── Ease.java
│ │ │ │ ├── KeyguardClockContainerHook.kt
│ │ │ │ └── MiuiKeyguardClockHook.java
│ │ │ ├── v20190507
│ │ │ │ ├── ChooseKeyguardClockActivityHook.kt
│ │ │ │ ├── MiuiKeyguardBaseClockHook.java
│ │ │ │ ├── MiuiKeyguardLeftTopClockHook.java
│ │ │ │ └── MiuiKeyguardVerticalClockHook.java
│ │ │ └── v20191213
│ │ │ │ ├── MiuiBaseClockHook.kt
│ │ │ │ ├── MiuiCenterHorizontalClockHook.kt
│ │ │ │ ├── MiuiLeftTopClockHook.kt
│ │ │ │ ├── MiuiLeftTopLargeClockHook.kt
│ │ │ │ └── MiuiVerticalClockHook.kt
│ │ │ ├── screen
│ │ │ ├── IntentAction.kt
│ │ │ ├── ScreenBroadcastManager.kt
│ │ │ ├── ScreenListener.kt
│ │ │ └── SimpleScreenListener.kt
│ │ │ ├── statusbar
│ │ │ ├── def
│ │ │ │ ├── BatteryMeterViewHook.java
│ │ │ │ ├── CollapsedStatusBarFragmentHook.java
│ │ │ │ ├── HeaderViewHook.java
│ │ │ │ ├── PhoneStatusBarViewHook.java
│ │ │ │ ├── SignalClusterViewHook.java
│ │ │ │ └── StatusBarClockHook.java
│ │ │ └── v20201109
│ │ │ │ ├── CollapsedStatusBarFragmentHook20201109.kt
│ │ │ │ ├── MiuiQSHeaderViewHook20201109.kt
│ │ │ │ ├── StatusBarClockHook20201109.java
│ │ │ │ ├── StatusBarMobileViewHook20201109.kt
│ │ │ │ └── StatusBarSignalPolicyHook20201109.kt
│ │ │ ├── tick
│ │ │ ├── TickObserver.java
│ │ │ └── TimeTicker.java
│ │ │ └── weather
│ │ │ ├── WeatherMonitor.java
│ │ │ └── WeatherObserver.java
│ │ ├── utils
│ │ └── appinfo
│ │ │ ├── AppInfo.kt
│ │ │ ├── AppInfoHelper.kt
│ │ │ └── AppVersionConst.java
│ │ └── wrapper
│ │ ├── MethodHookWrapper.java
│ │ └── XposedWrapper.java
│ └── res
│ ├── color
│ └── tag_view_text_color_selector.xml
│ ├── drawable
│ └── tag_view_bg.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── dialog_hitokoto_settings.xml
│ ├── preference_category.xml
│ ├── tag_view.xml
│ └── toolbar.xml
│ ├── menu
│ └── main_menu.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── values-sw360dp-v13
│ └── values-preference.xml
│ ├── values-zh-rCN
│ └── strings.xml
│ ├── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── common_strings.xml
│ ├── dimens.xml
│ ├── ic_launcher_background.xml
│ ├── ids.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ ├── dropdown_statusbar_settings.xml
│ ├── keyguard_settings.xml
│ ├── main_settings.xml
│ └── statusbar_settings.xml
├── art
├── cn
│ ├── 01.png
│ └── 02.png
└── en
│ ├── 01.png
│ └── 02.png
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── notes.md
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # IntelliJ
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/assetWizardSettings.xml
41 | .idea/dictionaries
42 | .idea/libraries
43 | .idea/caches
44 |
45 | # Keystore files
46 | # Uncomment the following line if you do not want to check your keystore files in.
47 | #*.jks
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
66 |
67 |
68 | *.iml
69 | .gradle
70 | /local.properties
71 | /.idea/caches
72 | /.idea/libraries
73 | /.idea/modules.xml
74 | /.idea/workspace.xml
75 | /.idea/navEditor.xml
76 | /.idea/assetWizardSettings.xml
77 | /.idea/*
78 | .DS_Store
79 | /build
80 | /captures
81 | .externalNativeBuild
82 |
83 | # eclipse
84 | .settings/
85 | *.project
86 | */.classpath
87 |
88 | # vscode
89 | .vscode/*
90 |
--------------------------------------------------------------------------------
/Functions-log.md:
--------------------------------------------------------------------------------
1 | # MIUI 12 , 2020.4.30 for mix2s - SystemUI(versionCode=201912130)
2 | - 状态栏
3 | - 时间显秒: 1
4 | - 时间对齐方式: 1
5 | - 自定义时间颜色: 1
6 | - 自定义时间格式: 1
7 | - 始终显示状态栏时间: 1
8 | - 信号居左: 0 - 信号居左之后,锁屏状态栏信号与运营商重叠
9 | - 信号双层显示:
10 | - 隐藏vpn图标:1
11 | - 隐藏HD图标:1
12 | - 电量百分比符号变小:无需此功能
13 | - 自定义移动网络类型:1
14 | - 下拉状态栏
15 | - 时间显秒: 1
16 | - 自定义时间颜色:1
17 | - 自定义日期颜色: 1
18 | - 显示天气信息: 0 - 功能失效
19 | - 天气颜色:
20 | - 天气字体大小
21 | - 锁屏界面
22 | - 水平时钟显秒: 0 - 功能失效 - 已修复
23 | - 垂直时钟显秒: 0 - 功能失效 - 已修复
24 | - 锁屏一言: 0 - 功能失效
25 |
26 | # SystemUI(versionCode=202011090) - Miui(12.5 21.1.28) - Android 11
27 | - 状态栏
28 | - 时间显秒: 有效
29 | - 时间对齐方式: 失效
30 | - 自定义时间颜色: 有效
31 | - 自定义时间格式: 有效
32 | - 始终显示状态栏时间: 失效
33 | - 信号居左: 失效
34 | - 信号双层显示:失效
35 | - 隐藏vpn图标:有效
36 | - 隐藏HD图标:失效
37 | - 电量百分比符号变小:无需
38 | - 自定义移动网络类型:有效
39 | - 下拉状态栏
40 | - 时间显秒: 有效
41 | - 自定义时间颜色:有效
42 | - 自定义日期颜色: 有效
43 | - 显示天气信息: 有效
44 | - 天气颜色: 有效
45 | - 天气字体大小: 有效
46 | - 锁屏界面
47 | - 水平时钟显秒: 有效
48 | - 垂直时钟显秒: 有效
49 | - 锁屏一言: 有效
50 |
--------------------------------------------------------------------------------
/LOG-CN.md:
--------------------------------------------------------------------------------
1 | # 更新日志
2 | - 21.11.02 1.4.0
3 | 1. 适配 MIUI 12.5 (21.10.13版本)
4 | 2. 修复: 天气显示问题
5 | 3. 修复: 始终显示状态栏时间问题
6 | - 21.03.04 1.3.0
7 | 1. 适配 MIUI 12.5+
8 | - 20.05.11 1.2.3
9 | 1. 修复: 锁屏居中时钟样式,一言居中问题
10 | - 20.05.10 1.2.2
11 | 1. 适配 MIUI 12: 锁屏时钟显秒
12 | 2. 适配 MIUI 12: 锁屏一言
13 | - 20.04.19 1.2.1
14 | 1. 新增可选项:自定义一言文字大小 和 文字颜色
15 | 2. 修复:尝试修复信号居左失效问题
16 | - 20.04.18 1.2.0
17 | 1. 新增可选项:锁屏一言
18 | - 19.06.03 1.1.0
19 | 1. 新增可选项:隐藏 HD 图标
20 | 2. 新增可选项:自定义下拉天气字体颜色和大小
21 | 3. 新增可选项:显示小号的电量百分比符号
22 | - 19.05.31 1.0.9
23 | 1. 新增可选项:下拉显示天气
24 | 2. 修复:信号左移时位置偏上的问题
25 | - 19.05.27 1.0.8
26 | 1. 修复:9.5.7 之前信号居左失效问题
27 | 2. 修复:9.5.7 之前信号双层显示失效问题
28 | - 19.05.27 1.0.7
29 | 1. 添加QQ群反馈交流入口
30 | - 19.05.26 1.0.6
31 | 1. 修复:EdXposed重启后需要重启SystemUI的问题
32 | 2. 修复:切换锁屏时钟样式后,显秒失效问题
33 | 3. 新增可选项:信号居左
34 | 4. 新增可选项:信号双层显示
35 | 5. 新增可选项:隐藏状态栏VPN图标
36 | 6. 新增可选项:自定义状态栏显示的移动网络类型
37 | 7. 优化:状态栏时间常显实现方式
38 | - 19.05.15 1.0.5
39 | 1. 修复:部分条件下,分钟显示不准确的问题
40 | 2. 新增可选项:系统桌面始终显示状态栏时间(即便桌面上有时钟小部件)
41 | - 19.05.14 1.0.4
42 | 1. 适配 MIUI10 9.5.7 版本,包括锁屏显秒,状态栏显秒,时间居中等
43 | 2. 修复跳秒问题
44 | 3. 优化显秒机制,提高模块性能
45 | - 19.04.29 1.0.3
46 | 1. 新增:状态栏自定义时间颜色
47 | 2. 新增:下拉状态栏自定义时间颜色
48 | 3. 优化:优化显秒机制
49 | 3. 新增:支持英文
50 | - 19.04.22 1.0.2
51 | 1. Bug修复:锁屏秒数适配亮暗模式
52 | 2. 新增:自定义状态栏时间对齐方式
53 | 3. 新增:自定义状态栏时间格式
54 | 4. 新增: 提供一个主开关
55 | - 19.04.19 1.0.1
56 | 1. 优化:状态栏显秒性能优化
57 | 2. 修复:处理显秒时的秒数重复问题
58 | 3. 修复:横屏状态下下拉通知栏显秒问题
59 | 4. 新增:添加太极用户提示入口,添加支付宝捐赠入口
60 | - 19.04.17 1.0.0
61 | 1. 第一个版本
--------------------------------------------------------------------------------
/LOG-EN.md:
--------------------------------------------------------------------------------
1 | # Update Logs
2 | - 21.11.02 1.4.0
3 | 1. Adapt to MIUI 12.5 (21.10.13)
4 | 2. Fix: weather info showing issues.
5 | 3. Fix: always show status bar clock in status bar.
6 | - 21.03.04 1.3.0
7 | 1. Adapt MIUI 12.5+
8 | - 20.05.11 1.2.3
9 | 1. Fix: the issue of Hitokoto alignment
10 | - 20.05.10 1.2.2
11 | 1. Adapt MIUI 12: Show seconds in lock-screen clock
12 | 2. Adapt MIUI 12: Hitokoto for lock-screen
13 | - 20.04.19 1.2.1
14 | 1. New option: custom hitokoto text size and color
15 | 2. Fix: signal align left invalid issue
16 | - 20.04.18 1.2.0
17 | 1. New option: Hitokoto on lock screen.
18 | - 19.06.03 1.1.0
19 | 1. New option: hide HD icon.
20 | 2. New option: custom weather info text color and size.
21 | 3. New option: show small battery percent sign.
22 | - 19.05.31 1.0.9
23 | 1. New option: show weather info in dropdown status bar.
24 | 2. Bug fixes: align top & left issue about mobile signal icons.
25 | - 19.05.27 1.0.8
26 | 1. Bug fixes: signal align left invalid issue before MIUI 10 9.5.7
27 | 2. Bug fixes: dual mobile signal invalid issue before MIUI 10 9.5.7
28 | - 19.05.27 1.0.7
29 | 1. New entry: join QQ group for communication and bug report.
30 | - 19.05.26 1.0.6
31 | 1. Bug fixes: invalid issue after rebooting.
32 | 2. Bug fixes: showing seconds invalid issue after choosing an new clock style in lock screen.
33 | 3. New option: align signal cluster to the left of status bar.
34 | 4. New option: show dual mobile signal in status bar.
35 | 5. New option: hide VPN icon in status bar.
36 | 6. New option: custom displayed mobile network type.
37 | 7. Optimization: no longer hook MIUI Launcher.
38 | - 19.05.15 1.0.5
39 | 1. Bug fixes: wrong minute display issue
40 | 2. New option: always show status bar clock in MIUI System Launcher
41 | - 19.05.14 1.0.4
42 | 1. Adapt to MIUI 9.5.7+.
43 | 2. Fix the leap seconds issue.
44 | 3. Optimize the mechanism of showing seconds. Improve the performance.
45 | - 19.04.29 1.0.3
46 | 1. New option: custom status bar clock color
47 | 2. New option: custom dropdown status bar clock color
48 | 3. Optimization: optimize the mechanism of showing seconds
49 | 4. New: support English
50 | - 19.04.22 1.0.2
51 | 1. Bug fixes: seconds view adapt to dark mode issue
52 | 2. New: show status bar clock by its alignment.
53 | 3. New: custom statuc bar clock time format.
54 | 4. New: provide an main switch.
55 | - 19.04.19 1.0.1
56 | 1. Optimization: improve the performance of showing seconds.
57 | 2. Bug fixes: duplicated seonds showing issue
58 | 3. Bug fixes: show seconds on notification panel on horizontal screen.
59 | 4. Add: the entry for alipay donation and taichi users notice.
60 | - 19.04.17 1.0.0
61 | 1. First version
--------------------------------------------------------------------------------
/README-CN.md:
--------------------------------------------------------------------------------
1 | # XMiTools
2 | MIUI 10 / MIUI 11 / MIUI 12 / MIUI 12.5 系统界面模块
3 |
4 | [English README](/README-EN.md)
5 | # 效果截图
6 | 
7 |
8 | # 下载
9 | 可在以下地方下载:
10 | - [releases](/releases)
11 | - [酷安](https://www.coolapk.com/apk/com.tianma.tweaks.miui)
12 | - [Xposed仓库](https://repo.xposed.info/module/com.tianma.tweaks.miui)
13 |
14 | # 注意
15 | - 仅适用于MIUI,其他版本的系统,请慎用。
16 | - 支持 Xposed,EdXposed,LSPosed 和 太极阳。
17 |
18 | # 功能
19 | - 状态栏
20 | 1. 状态栏显示秒数
21 | 2. 状态栏时间对齐方式(居左,居中,居右)
22 | 3. 自定义时间格式
23 | 4. 自定义时间颜色
24 | 5. 信号居左
25 | 6. 信号双层显示
26 | 7. 自定义显示的移动网络类型
27 | - 下拉状态栏
28 | 1. 时间显示秒数
29 | 2. 自定义时间,日期颜色
30 | 3. 显示天气信息
31 | - 锁屏界面
32 | 1. 水平时钟显示秒数
33 | 2. 垂直时钟显示秒数
34 | - 等等...
35 |
36 | # 感谢
37 | - [custoMIUIzer](https://code.highspec.ru/Mikanoshi/CustoMIUIzer/)
38 | - [GravityBox](https://github.com/GravityBox/GravityBox)
39 | - [Xposed](https://github.com/rovo89/Xposed)
40 | - [Material Dialogs](https://github.com/afollestad/material-dialogs)
41 | - [Android Shell](https://github.com/jaredrummler/AndroidShell)
42 |
43 | # 协议
44 | 本源码遵循 [GPLv3](https://www.gnu.org/licenses/gpl-3.0.txt) 协议
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # XMiTools
2 | An xposed module for MIUI 10 / MIUI 11 / MIUI 12 / MIUI 12.5 SystemUI.
3 |
4 | [中文说明](/README-CN.md)
5 | # Screenshots
6 | 
7 |
8 | # Download
9 | You can download in the following sites:
10 | - [GitHub releases](/releases)
11 | - [CoolApk](https://www.coolapk.com/apk/com.tianma.tweaks.miui)
12 | - [Xposed Repository](https://repo.xposed.info/module/com.tianma.tweaks.miui)
13 |
14 | # Attention
15 | - Only compatible for MIUI. Other ROM may not suitable.
16 | - Support Xposed, EdXposed, LSPosed and TaiChi.
17 |
18 | # Features
19 | - Status Bar
20 | 1. Show seconds in status bar clock
21 | 2. Custom clock alignment (left, center, right)
22 | 3. Custom clock time format
23 | 4. Custom clock time color
24 | 5. Signal icons align left
25 | 6. Display dual mobile signals
26 | 7. Custom displayed mobile network type
27 | - Dropdown Status Bar
28 | 1. Show seconds in dropdown status bar clock
29 | 2. Custom clock time and date color
30 | 3. Show weather info
31 | - Lock Screen
32 | 1. Show seconds in horizontal clock
33 | 2. Show seconds in vertical clock
34 | - And so on...
35 |
36 |
37 | # Credits
38 | - [custoMIUIzer](https://code.highspec.ru/Mikanoshi/CustoMIUIzer/)
39 | - [GravityBox](https://github.com/GravityBox/GravityBox)
40 | - [Xposed](https://github.com/rovo89/Xposed)
41 | - [Material Dialogs](https://github.com/afollestad/material-dialogs)
42 | - [Android Shell](https://github.com/jaredrummler/AndroidShell)
43 |
44 | # License
45 | All code is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.txt)
46 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
3 | /signature.properties
4 |
--------------------------------------------------------------------------------
/app/and-res-guard.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'AndResGuard'
2 |
3 | andResGuard {
4 | // mappingFile = file("./resource_mapping.txt")
5 | mappingFile = null
6 | use7zip = true
7 | useSign = true
8 | // 打开这个开关,会keep住所有资源的原始路径,只混淆资源的名字
9 | keepRoot = false
10 | whiteList = [
11 | // for your icon
12 | "R.mipmap.ic_launcher",
13 | "R.mipmap.ic_launcher_round",
14 | "R.mipmap.ic_launcher_foreground",
15 |
16 | // Umeng sdk
17 | "R.anim.umeng*",
18 | "R.string.umeng*",
19 | "R.string.UM*",
20 | "R.string.tb_*",
21 | "R.layout.umeng*",
22 | "R.layout.socialize_*",
23 | "R.layout.*messager*",
24 | "R.layout.tb_*",
25 | "R.color.umeng*",
26 | "R.color.tb_*",
27 | "R.style.*UM*",
28 | "R.style.umeng*",
29 | "R.drawable.umeng*",
30 | "R.drawable.tb_*",
31 | "R.drawable.sina*",
32 | "R.drawable.qq_*",
33 | "R.drawable.tb_*",
34 | "R.id.umeng*",
35 | "R.id.*messager*",
36 | "R.id.progress_bar_parent",
37 | "R.id.socialize_*",
38 | "R.id.webView",
39 |
40 | // Google service
41 | "R.string.google_app_id",
42 | "R.string.gcm_defaultSenderId",
43 | "R.string.default_web_client_id",
44 | "R.string.ga_trackingId",
45 | "R.string.firebase_database_url",
46 | "R.string.google_api_key",
47 | "R.string.google_crash_reporting_api_key",
48 |
49 | // getui
50 | "R.drawable.push",
51 | "R.drawable.push_small",
52 | "R.layout.getui_notification",
53 |
54 | // JPush
55 | "R.drawable.jpush_notification_icon",
56 |
57 | // GrowingIO
58 | "R.string.growingio_project_id",
59 | "R.string.growingio_url_scheme",
60 | "R.string.growingio_channel",
61 | ]
62 | compressFilePattern = [
63 | "*.png",
64 | "*.jpg",
65 | "*.jpeg",
66 | "*.gif",
67 | ]
68 | sevenzip {
69 | artifact = 'com.tencent.mm:SevenZip:1.2.17'
70 | //path = "/usr/local/bin/7za"
71 | }
72 |
73 | /**
74 | * 可选: 如果不设置则会默认覆盖assemble输出的apk
75 | **/
76 | // finalApkBackupPath = "${project.rootDir}/final.apk"
77 |
78 | /**
79 | * 可选: 指定v1签名时生成jar文件的摘要算法
80 | * 默认值为“SHA-1”
81 | **/
82 | // digestalg = "SHA-256"
83 | }
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply from: 'and-res-guard.gradle'
5 |
6 | def keyFile = file(findProperty("tianma.keystore.path") as String)
7 | def propFile = file(findProperty("tianma.signature.path") as String)
8 |
9 | def keyProps = new Properties()
10 | if (propFile.exists()) {
11 | keyProps.load(new FileInputStream(propFile))
12 | }
13 |
14 | def static releaseTime() {
15 | return new Date().format("yyMMdd", TimeZone.default)
16 | }
17 |
18 | ext {
19 | VERSION_CODE = 17
20 | VERSION_NAME = "1.4.0"
21 | }
22 |
23 | android {
24 | compileSdkVersion 28
25 | defaultConfig {
26 | applicationId "com.tianma.tweaks.miui"
27 | minSdkVersion 21
28 | targetSdkVersion 27
29 | versionCode VERSION_CODE
30 | versionName VERSION_NAME
31 |
32 | buildConfigField("String", "LOG_TAG", "\"XMiTools\"")
33 | buildConfigField("boolean", "LOG_TO_XPOSED", "true")
34 | buildConfigField("boolean", "LOG_TO_EDXPOSED", "true")
35 | buildConfigField("int", "MODULE_VERSION", "" + VERSION_CODE)
36 | }
37 |
38 | lintOptions {
39 | disable 'MissingTranslation'
40 | }
41 |
42 | signingConfigs {
43 | release {
44 | storeFile keyFile
45 | storePassword keyProps['STORE_PASSWORD']
46 | keyAlias keyProps['KEY_ALIAS']
47 | keyPassword keyProps['KEY_PASSWORD']
48 | }
49 | }
50 |
51 | buildTypes {
52 | debug {
53 | buildConfigField("int", "LOG_LEVEL", "2")
54 |
55 | if (keyFile.exists()) {
56 | println "using keystore"
57 | signingConfig signingConfigs.release
58 | }
59 |
60 | debuggable false
61 | }
62 |
63 | release {
64 | buildConfigField("int", "LOG_LEVEL", "4")
65 |
66 | minifyEnabled true
67 | shrinkResources true
68 | zipAlignEnabled true
69 |
70 | if (keyFile.exists()) {
71 | println "using keystore"
72 | signingConfig signingConfigs.release
73 | }
74 |
75 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
76 | }
77 | }
78 |
79 | compileOptions {
80 | sourceCompatibility JavaVersion.VERSION_1_8
81 | targetCompatibility JavaVersion.VERSION_1_8
82 | }
83 | kotlinOptions {
84 | jvmTarget = '1.8'
85 | }
86 |
87 | applicationVariants.all { variant ->
88 | variant.outputs.all { output ->
89 | output.outputFileName = "XMiTools_${variant.versionName.replaceAll('\\s+', '_')}_${releaseTime()}_${variant.buildType.name.charAt(0)}.apk"
90 | }
91 | }
92 | }
93 |
94 | ext {
95 | gsonVersion = "2.8.6" // gson
96 |
97 | rxJavaVersion = "2.2.17" // RxJava 2
98 | rxAndroidVersion = "2.1.1" // RxAndroid 2
99 |
100 | okHttpVersion = "4.4.0" // OkHttp
101 | retrofitVersion = "2.5.0" // retrofit
102 |
103 | materialDialogsVersion = "3.3.0"
104 | }
105 |
106 | dependencies {
107 | implementation fileTree(dir: 'libs', include: ['*.jar'])
108 | implementation 'androidx.appcompat:appcompat:1.2.0'
109 | implementation 'com.google.android.material:material:1.3.0' // material design support
110 | implementation 'androidx.preference:preference-ktx:1.1.1' // preference-v7
111 | implementation 'androidx.browser:browser:1.3.0' // custom tabs
112 | implementation "androidx.constraintlayout:constraintlayout:2.0.4" // constraint layout
113 |
114 | // Xposed
115 | compileOnly 'de.robv.android.xposed:api:82'
116 | compileOnly 'de.robv.android.xposed:api:82:sources'
117 |
118 | // ButterKnife
119 | // implementation 'com.jakewharton:butterknife:10.2.0'
120 | // annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
121 |
122 | // Android Shell
123 | implementation 'com.jaredrummler:android-shell:1.0.0'
124 |
125 | // Material Dialogs
126 | implementation "com.afollestad.material-dialogs:core:$materialDialogsVersion"
127 |
128 | // color picker preference
129 | implementation 'com.jaredrummler:colorpicker:1.1.0'
130 |
131 | // Gson
132 | implementation "com.google.code.gson:gson:$gsonVersion"
133 |
134 | // RxJava 2
135 | implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
136 | // RxAndroid 2
137 | implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
138 |
139 | // OkHttp
140 | implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
141 | // OkHttp logging interceptor
142 | implementation "com.squareup.okhttp3:logging-interceptor:$okHttpVersion"
143 |
144 | // Retrofit
145 | implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
146 | // Retrofit Gson converter
147 | implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
148 | // Retrofit scalars converter
149 | implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion"
150 | // Retrofit RxJava Adapter
151 | implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"
152 |
153 | // Kotlin
154 | implementation "androidx.core:core-ktx:1.3.0"
155 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
156 |
157 | // flex-box layout
158 | implementation 'com.google.android:flexbox:2.0.1'
159 | }
160 | repositories {
161 | mavenCentral()
162 | }
163 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -keepclasseswithmembers class * implements de.robv.android.xposed.IXposedHookLoadPackage {
24 | public void handleLoadPackage(...);
25 | }
26 |
27 | -keepclasseswithmembers class * implements de.robv.android.xposed.IXposedHookZygoteInit {
28 | public void initZygote(...);
29 | }
30 |
31 | -keep class com.tianma.tweaks.miui.utils.ModuleUtils {
32 | int getModuleVersion();
33 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
15 |
16 |
19 |
22 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | com.tianma.tweaks.miui.xp.HookEntry
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/App.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app
2 |
3 | import android.app.Application
4 |
5 | /**
6 | * desc: Application instance
7 | * date: 2021/10/7
8 | */
9 | class App : Application() {
10 |
11 | companion object {
12 | lateinit var appContext: Application
13 | private set
14 | }
15 |
16 | override fun onCreate() {
17 | super.onCreate()
18 |
19 | appContext = this
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.view.Menu
7 | import android.view.MenuItem
8 | import com.afollestad.materialdialogs.MaterialDialog
9 | import com.tianma.tweaks.miui.R
10 | import com.tianma.tweaks.miui.app.base.BaseActivity
11 | import com.tianma.tweaks.miui.app.fragment.*
12 | import com.tianma.tweaks.miui.utils.*
13 | import kotlinx.android.synthetic.main.activity_main.*
14 | import kotlinx.android.synthetic.main.toolbar.*
15 |
16 | /**
17 | * HomeActivity
18 | */
19 | class MainActivity : BaseActivity() {
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | setContentView(R.layout.activity_main)
24 | setupToolbar()
25 | initFragments()
26 | showModuleStatus()
27 | }
28 |
29 | private fun setupToolbar() {
30 | setSupportActionBar(toolbar)
31 | }
32 |
33 | private fun initFragments() {
34 | val fragments = mutableListOf().apply {
35 | add(GeneralSettingsFragment(getString(R.string.pref_general_title)))
36 | add(StatusBarSettingsFragment(getString(R.string.pref_status_bar_title)))
37 | add(DropDownStatusBarSettingsFragment(getString(R.string.pref_dropdown_status_bar_title)))
38 | add(KeyguardSettingsFragment(getString(R.string.pref_keyguard_title)))
39 | }
40 |
41 | viewPager.adapter = SettingsFragmentPagerAdapter(supportFragmentManager, fragments)
42 | tabLayout.setupWithViewPager(viewPager)
43 | }
44 |
45 | override fun onCreateOptionsMenu(menu: Menu?): Boolean {
46 | menuInflater.inflate(R.menu.main_menu, menu)
47 | return true
48 | }
49 |
50 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
51 | when (item.itemId) {
52 | R.id.action_reboot_system -> performRebootSystem()
53 | R.id.action_soft_reboot_system -> preformSoftRebootSystem()
54 | R.id.action_restart_host_apps -> performRestartHostApps()
55 | R.id.action_taichi_users_notice -> showTaiChiUsersNotice()
56 | R.id.action_edxposed_users_notice -> showEdXposedUsersNotice()
57 | else -> return super.onOptionsItemSelected(item)
58 | }
59 | return true
60 | }
61 |
62 | private fun performRebootSystem() {
63 | MaterialDialog(this).show {
64 | title(R.string.action_reboot_system)
65 | message(R.string.prompt_reboot_system_message)
66 | positiveButton(R.string.confirm) {
67 | RootUtils.reboot()
68 | }
69 | negativeButton(R.string.cancel)
70 | }
71 | }
72 |
73 | private fun preformSoftRebootSystem() {
74 | MaterialDialog(this).show {
75 | title(R.string.action_soft_reboot_system)
76 | message(R.string.prompt_soft_reboot_message)
77 | positiveButton(R.string.confirm) {
78 | RootUtils.softReboot()
79 | }
80 | negativeButton(R.string.cancel)
81 | }
82 | }
83 |
84 | private fun performRestartHostApps() {
85 | MaterialDialog(this).show {
86 | title(R.string.action_restart_host_apps)
87 | message(R.string.prompt_restart_host_apps_message)
88 | positiveButton(R.string.confirm) {
89 | RootUtils.restartSystemUI()
90 | }
91 | negativeButton(R.string.cancel)
92 | }
93 | }
94 |
95 | private fun showTaiChiUsersNotice() {
96 | MaterialDialog(this).show {
97 | title(R.string.action_taichi_users_notice)
98 | message(R.string.prompt_taichi_users_notice_message)
99 | positiveButton(R.string.check_module) {
100 | PackageUtils.startCheckModuleInTaiChi(this@MainActivity)
101 | }
102 | negativeButton(R.string.add_applications) {
103 | PackageUtils.startAddAppsInTaiChi(this@MainActivity)
104 | }
105 | }
106 | }
107 |
108 | private fun showEdXposedUsersNotice() {
109 | MaterialDialog(this).show {
110 | title(R.string.action_edxposed_users_notice)
111 | message(R.string.edxposed_users_notice_content)
112 | positiveButton(R.string.confirm)
113 | }
114 | }
115 |
116 | private fun showModuleStatus() {
117 | val handler = Handler(Looper.getMainLooper())
118 | handler.postDelayed({
119 | if (isFinishing) {
120 | return@postDelayed
121 | }
122 | val format = "%s (%s)"
123 | val appName = getString(R.string.app_name)
124 | val appTitle = if (ModuleUtils.isModuleActive()) {
125 | String.format(format, appName, getString(R.string.module_status_active))
126 | } else {
127 | String.format(format, appName, getString(R.string.module_status_inactive))
128 | }
129 | title = appTitle
130 | }, 1000L)
131 | }
132 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.base
2 |
3 | import android.content.Context
4 | import android.view.MenuItem
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.tianma.tweaks.miui.utils.ContextUtils
7 |
8 | abstract class BaseActivity : AppCompatActivity() {
9 |
10 | override fun attachBaseContext(newBase: Context?) {
11 | val context = if (newBase != null) {
12 | ContextUtils.getProtectedContextIfNecessary(newBase)
13 | } else {
14 | newBase
15 | }
16 | super.attachBaseContext(context)
17 | }
18 |
19 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
20 | val itemId = item.itemId
21 | if (itemId == android.R.id.home) {
22 | onBackPressed()
23 | return true
24 | }
25 | return super.onOptionsItemSelected(item)
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/base/BasePreferenceFragment.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.base
2 |
3 | import android.os.Bundle
4 | import androidx.preference.PreferenceFragmentCompat
5 | import com.tianma.tweaks.miui.cons.AppConst
6 |
7 | abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
8 |
9 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
10 | val pm = preferenceManager
11 | pm.sharedPreferencesName = AppConst.XMI_TOOLS_PREFS_NAME
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/fragment/BaseSettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.fragment
2 |
3 | import com.tianma.tweaks.miui.app.base.BasePreferenceFragment
4 | import com.tianma.tweaks.miui.cons.AppConst
5 | import com.tianma.tweaks.miui.utils.ContextUtils
6 | import com.tianma.tweaks.miui.utils.StorageUtils
7 |
8 | /**
9 | * Base Fragment for settings.
10 | */
11 | abstract class BaseSettingsFragment @JvmOverloads constructor(val title: CharSequence? = "") :
12 | BasePreferenceFragment() {
13 |
14 | override fun onPause() {
15 | super.onPause()
16 | setPreferenceWorldWritable()
17 | }
18 |
19 | private fun setPreferenceWorldWritable() {
20 | val activity = activity ?: return
21 | val context = ContextUtils.getProtectedContextIfNecessary(activity.applicationContext)
22 |
23 | val prefsFile = StorageUtils.getSharedPreferencesFile(context, AppConst.XMI_TOOLS_PREFS_NAME)
24 | StorageUtils.setFileWorldWritable(prefsFile, 2)
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/fragment/DropDownStatusBarSettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.fragment
2 |
3 | import android.os.Bundle
4 | import android.text.InputType
5 | import androidx.preference.EditTextPreference
6 | import androidx.preference.Preference
7 | import com.tianma.tweaks.miui.R
8 | import com.tianma.tweaks.miui.cons.PrefConst
9 |
10 | /**
11 | * dSettings fragment for System DropDown StatusBar
12 | */
13 | class DropDownStatusBarSettingsFragment(title: CharSequence? = ""): BaseSettingsFragment(title), Preference.OnPreferenceChangeListener {
14 |
15 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
16 | super.onCreatePreferences(savedInstanceState, rootKey)
17 | addPreferencesFromResource(R.xml.dropdown_statusbar_settings)
18 |
19 | val weatherTextSizePref = findPreference(PrefConst.DROPDOWN_STATUS_BAR_WEATHER_TEXT_SIZE)
20 | weatherTextSizePref?.let {
21 | weatherTextSizePref.setOnBindEditTextListener { editText ->
22 | editText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
23 | editText.setSelection(editText.text.length)
24 | }
25 | weatherTextSizePref.onPreferenceChangeListener = this
26 | }
27 | }
28 |
29 | override fun onResume() {
30 | super.onResume()
31 |
32 | val weatherTextSizePref = findPreference(PrefConst.DROPDOWN_STATUS_BAR_WEATHER_TEXT_SIZE)
33 | weatherTextSizePref?.let {
34 | showWeatherTextSize(it, it.text)
35 | }
36 | }
37 |
38 | override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean {
39 | val key = preference?.key
40 | if (PrefConst.DROPDOWN_STATUS_BAR_WEATHER_TEXT_SIZE == key) {
41 | val value = newValue as String?
42 | if (value.isNullOrEmpty()) {
43 | return false
44 | } else {
45 | showWeatherTextSize(preference, value)
46 | }
47 | } else {
48 | return false
49 | }
50 | return true
51 | }
52 |
53 | private fun showWeatherTextSize(preference: Preference, newValue: String) {
54 | preference.summary = newValue
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/fragment/GeneralSettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.fragment
2 |
3 | import android.app.Activity
4 | import android.content.ComponentName
5 | import android.content.pm.PackageManager
6 | import android.os.Bundle
7 | import androidx.preference.Preference
8 | import com.tianma.tweaks.miui.BuildConfig
9 | import com.tianma.tweaks.miui.R
10 | import com.tianma.tweaks.miui.cons.AppConst
11 | import com.tianma.tweaks.miui.cons.PrefConst
12 | import com.tianma.tweaks.miui.utils.PackageUtils
13 | import com.tianma.tweaks.miui.utils.Utils
14 |
15 | class GeneralSettingsFragment(title: CharSequence? = "") : BaseSettingsFragment(title), Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
16 |
17 | private lateinit var mActivity: Activity
18 |
19 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
20 | super.onCreatePreferences(savedInstanceState, rootKey)
21 | addPreferencesFromResource(R.xml.main_settings)
22 |
23 | findPreference(PrefConst.HIDE_LAUNCHER_ICON)?.onPreferenceChangeListener = this
24 |
25 | findPreference(PrefConst.SOURCE_CODE)?.onPreferenceClickListener = this
26 | findPreference(PrefConst.KEY_JOIN_QQ_GROUP)?.onPreferenceClickListener = this
27 | findPreference(PrefConst.DONATE_BY_ALIPAY)?.onPreferenceClickListener = this
28 | }
29 |
30 | override fun onActivityCreated(savedInstanceState: Bundle?) {
31 | super.onActivityCreated(savedInstanceState)
32 | mActivity = requireActivity()
33 | }
34 |
35 | override fun onResume() {
36 | super.onResume()
37 |
38 | showVersionInfo()
39 | }
40 |
41 | override fun onPreferenceClick(preference: Preference?): Boolean {
42 | when (preference?.key) {
43 | PrefConst.SOURCE_CODE -> {
44 | showSourceCode()
45 | }
46 | PrefConst.KEY_JOIN_QQ_GROUP -> {
47 | joinQQGroup()
48 | }
49 | PrefConst.DONATE_BY_ALIPAY -> {
50 | donateByAlipay()
51 | }
52 | else -> {
53 | return false
54 | }
55 | }
56 | return true
57 | }
58 |
59 | override fun onPreferenceChange(preference: Preference?, newValue: Any): Boolean {
60 | val key = preference?.key
61 | if (PrefConst.HIDE_LAUNCHER_ICON == key) {
62 | hideOrShowLauncherIcon(newValue as Boolean)
63 | } else {
64 | return false
65 | }
66 | return true
67 | }
68 |
69 | private fun hideOrShowLauncherIcon(hide: Boolean) {
70 | val pm = mActivity.packageManager
71 | val launcherCN = ComponentName(mActivity, AppConst.MAIN_ACTIVITY_ALIAS)
72 | val state = if (hide) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED
73 | if (pm.getComponentEnabledSetting(launcherCN) != state) {
74 | pm.setComponentEnabledSetting(launcherCN, state, PackageManager.DONT_KILL_APP)
75 | }
76 | }
77 |
78 | private fun showVersionInfo() {
79 | findPreference(PrefConst.APP_VERSION)?.summary = BuildConfig.VERSION_NAME
80 | }
81 |
82 | private fun showSourceCode() {
83 | Utils.showWebPage(activity, AppConst.PROJECT_SOURCE_CODE_URL)
84 | }
85 |
86 | private fun joinQQGroup() {
87 | PackageUtils.joinQQGroup(context)
88 | }
89 |
90 | private fun donateByAlipay() {
91 | PackageUtils.startAlipayDonatePage(context)
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/fragment/KeyguardSettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.fragment
2 |
3 | import android.os.Bundle
4 | import android.text.InputType
5 | import androidx.preference.EditTextPreference
6 | import androidx.preference.Preference
7 | import com.tianma.tweaks.miui.R
8 | import com.tianma.tweaks.miui.app.widget.dialog.OneSentenceSettingsDialogWrapper
9 | import com.tianma.tweaks.miui.cons.PrefConst
10 |
11 | /**
12 | * Settings fragment for LockScreen
13 | */
14 | class KeyguardSettingsFragment(title: CharSequence? = "") : BaseSettingsFragment(title), Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
15 |
16 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
17 | super.onCreatePreferences(savedInstanceState, rootKey)
18 |
19 | addPreferencesFromResource(R.xml.keyguard_settings)
20 |
21 | findPreference(PrefConst.ONE_SENTENCE_SETTINGS)?.onPreferenceClickListener = this
22 |
23 | val oneSentencePref = findPreference(PrefConst.ONE_SENTENCE_TEXT_SIZE)
24 | oneSentencePref?.let {
25 | oneSentencePref.setOnBindEditTextListener { editText ->
26 | editText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
27 | editText.setSelection(editText.text.length)
28 | }
29 | oneSentencePref.onPreferenceChangeListener = this
30 | }
31 | }
32 |
33 | override fun onResume() {
34 | super.onResume()
35 |
36 | val oneSentencePref = findPreference(PrefConst.ONE_SENTENCE_TEXT_SIZE)
37 | oneSentencePref?.let {
38 | showOneSentenceTextSize(it, it.text)
39 | }
40 | }
41 |
42 | override fun onPreferenceClick(preference: Preference?): Boolean {
43 | val key = preference?.key
44 | if (key == PrefConst.ONE_SENTENCE_SETTINGS) {
45 | onOneSentenceSettingsClicked()
46 | } else {
47 | return false
48 | }
49 | return true
50 | }
51 |
52 | override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean {
53 | val key = preference?.key
54 | if (PrefConst.ONE_SENTENCE_TEXT_SIZE == key) {
55 | val value = newValue as String?
56 | if (value.isNullOrEmpty()) {
57 | return false
58 | } else {
59 | showOneSentenceTextSize(preference, value)
60 | }
61 | }
62 | return true
63 | }
64 |
65 | private fun onOneSentenceSettingsClicked() {
66 | context?.let {
67 | OneSentenceSettingsDialogWrapper(it).show()
68 | }
69 | }
70 |
71 | private fun showOneSentenceTextSize(preference: Preference, newValue: String) {
72 | preference.summary = newValue
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/fragment/SettingsFragmentPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.fragment
2 |
3 | import androidx.fragment.app.FragmentManager
4 | import androidx.fragment.app.FragmentPagerAdapter
5 |
6 | class SettingsFragmentPagerAdapter(
7 | fm: FragmentManager,
8 | private var fragments: List
9 | ) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
10 |
11 | override fun getItem(position: Int) = fragments[position]
12 |
13 | override fun getCount() = fragments.size
14 |
15 | override fun getPageTitle(position: Int): CharSequence? {
16 | return fragments[position].title
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/fragment/StatusBarSettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.fragment
2 |
3 | import android.os.Bundle
4 | import androidx.preference.Preference
5 | import com.tianma.tweaks.miui.R
6 | import com.tianma.tweaks.miui.cons.PrefConst
7 |
8 | /**
9 | * Settings fragment for System StatusBar
10 | */
11 | class StatusBarSettingsFragment(title: CharSequence? = "") : BaseSettingsFragment(title), Preference.OnPreferenceChangeListener {
12 |
13 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
14 | super.onCreatePreferences(savedInstanceState, rootKey)
15 | addPreferencesFromResource(R.xml.statusbar_settings)
16 |
17 | findPreference(PrefConst.STATUS_BAR_CLOCK_FORMAT)?.onPreferenceChangeListener = this
18 | findPreference(PrefConst.CUSTOM_MOBILE_NETWORK_TYPE)?.onPreferenceChangeListener = this
19 | }
20 |
21 | override fun onResume() {
22 | super.onResume()
23 |
24 | val sp = preferenceManager.sharedPreferences
25 |
26 | val timeFormatPref = findPreference(PrefConst.STATUS_BAR_CLOCK_FORMAT)
27 | timeFormatPref?.let {
28 | val timeFormat = sp.getString(PrefConst.STATUS_BAR_CLOCK_FORMAT, PrefConst.STATUS_BAR_CLOCK_FORMAT_DEFAULT)
29 | showStatusBarClockFormat(timeFormatPref, timeFormat)
30 | }
31 |
32 | val networkTypePref = findPreference(PrefConst.CUSTOM_MOBILE_NETWORK_TYPE)
33 | networkTypePref?.let {
34 | val networkType = sp.getString(PrefConst.CUSTOM_MOBILE_NETWORK_TYPE, PrefConst.CUSTOM_MOBILE_NETWORK_TYPE_DEFAULT)
35 | showCustomMobileNetworkType(networkTypePref, networkType)
36 | }
37 | }
38 |
39 | override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean {
40 | val key = preference?.key
41 | when {
42 | PrefConst.STATUS_BAR_CLOCK_FORMAT == key -> {
43 | showStatusBarClockFormat(preference, newValue as String?)
44 | }
45 | PrefConst.CUSTOM_MOBILE_NETWORK_TYPE == key -> {
46 | showCustomMobileNetworkType(preference, newValue as String?)
47 | }
48 | else -> {
49 | return false
50 | }
51 | }
52 | return true
53 | }
54 |
55 | private fun showStatusBarClockFormat(preference: Preference, newValue: String?) {
56 | preference.summary = newValue
57 | }
58 |
59 | private fun showCustomMobileNetworkType(preference: Preference, newValue: String?) {
60 | preference.summary = newValue
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/widget/tag/ItemClickCallback.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.widget.tag
2 |
3 | import android.view.View
4 |
5 | interface ItemClickCallback {
6 |
7 | fun onItemClicked(itemView: View?, item: E?, position: Int)
8 |
9 | fun onItemLongClicked(itemView: View?, item: E?, position: Int): Boolean
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/widget/tag/TagAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.widget.tag
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.TextView
8 | import androidx.recyclerview.widget.RecyclerView
9 | import com.tianma.tweaks.miui.R
10 |
11 | class TagAdapter(private var context: Context, private var dataList: MutableList) : RecyclerView.Adapter() {
12 |
13 | var itemClickCallback: ItemClickCallback? = null
14 |
15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagViewHolder {
16 | val itemView = LayoutInflater.from(context).inflate(R.layout.tag_view, parent, false)
17 | return TagViewHolder(itemView)
18 | }
19 |
20 | override fun getItemCount(): Int {
21 | return dataList.size
22 | }
23 |
24 | override fun onBindViewHolder(holder: TagViewHolder, position: Int) {
25 | val tagBean = dataList[position]
26 | holder.bindData(tagBean, position)
27 | holder.bindListener(tagBean, position)
28 | }
29 |
30 | inner class TagViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
31 | private lateinit var tagView: TextView
32 |
33 | init {
34 | bindViews()
35 | }
36 |
37 | private fun bindViews() {
38 | tagView = itemView.findViewById(R.id.tag_text_view)
39 | }
40 |
41 | fun bindData(tagBean: TagBean, position: Int) {
42 | tagView.text = tagBean.value
43 |
44 | tagView.isSelected = tagBean.isSelected
45 | tagView.isEnabled = tagBean.isEnabled
46 | }
47 |
48 | fun bindListener(tagBean: TagBean, position: Int) {
49 | itemClickCallback?.let { itemClickCallback ->
50 | itemView.setOnClickListener { itemView ->
51 | itemClickCallback.onItemClicked(itemView, tagBean, position)
52 | }
53 |
54 | itemView.setOnLongClickListener { itemView ->
55 | itemClickCallback.onItemLongClicked(itemView, tagBean, position)
56 | }
57 | }
58 | }
59 | }
60 |
61 | fun getDataList(): List {
62 | return dataList
63 | }
64 |
65 | fun setDataList(list: MutableList) {
66 | dataList.clear()
67 | dataList.addAll(list)
68 | notifyDataSetChanged()
69 | }
70 |
71 | fun setAllSelected(selected: Boolean) {
72 | for (tagBean in dataList) {
73 | tagBean.isSelected = selected
74 | }
75 | notifyDataSetChanged()
76 | }
77 |
78 | fun setItemSelected(selected: Boolean, position: Int) {
79 | dataList[position].isSelected = selected
80 | notifyDataSetChanged()
81 | }
82 |
83 | fun setAllEnabled(enabled: Boolean) {
84 | for (tagBean in dataList) {
85 | tagBean.isEnabled = enabled
86 | }
87 | notifyDataSetChanged()
88 | }
89 |
90 |
91 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/app/widget/tag/TagBean.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.app.widget.tag;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class TagBean implements Parcelable {
7 |
8 | private String key;
9 | private String value;
10 | private boolean selected = false;
11 | private boolean enabled = true;
12 |
13 | public TagBean() {
14 | this(null, null);
15 | }
16 |
17 | public TagBean(String key, String value) {
18 | this.key = key;
19 | this.value = value;
20 | }
21 |
22 | public String getKey() {
23 | return key;
24 | }
25 |
26 | public void setKey(String key) {
27 | this.key = key;
28 | }
29 |
30 | public String getValue() {
31 | return value;
32 | }
33 |
34 | public void setValue(String value) {
35 | this.value = value;
36 | }
37 |
38 | public boolean isSelected() {
39 | return selected;
40 | }
41 |
42 | public void setSelected(boolean selected) {
43 | this.selected = selected;
44 | }
45 |
46 | public boolean isEnabled() {
47 | return enabled;
48 | }
49 |
50 | public void setEnabled(boolean enabled) {
51 | this.enabled = enabled;
52 | }
53 |
54 | @Override
55 | public int describeContents() {
56 | return 0;
57 | }
58 |
59 | @Override
60 | public void writeToParcel(Parcel dest, int flags) {
61 | dest.writeString(key);
62 | dest.writeString(value);
63 | dest.writeByte((byte) (selected ? 1 : 0));
64 | dest.writeByte((byte) (enabled ? 1 : 0));
65 | }
66 |
67 | protected TagBean(Parcel in) {
68 | key = in.readString();
69 | value = in.readString();
70 | selected = in.readByte() != 0;
71 | enabled = in.readByte() != 0;
72 | }
73 |
74 | public static final Creator CREATOR = new Creator() {
75 | @Override
76 | public TagBean createFromParcel(Parcel in) {
77 | return new TagBean(in);
78 | }
79 |
80 | @Override
81 | public TagBean[] newArray(int size) {
82 | return new TagBean[size];
83 | }
84 | };
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/cons/AppConst.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.cons
2 |
3 | import com.tianma.tweaks.miui.BuildConfig
4 |
5 | object AppConst {
6 | // MiTweaks begin
7 | const val MAIN_ACTIVITY_ALIAS = BuildConfig.APPLICATION_ID + ".app.MainActivityAlias"
8 | const val PROJECT_SOURCE_CODE_URL = "https://github.com/tianma8023/XMiTools"
9 | const val XMI_TOOLS_PREFS_NAME = BuildConfig.APPLICATION_ID + "_preferences"
10 | // MiTweaks end
11 |
12 | // Alipay begin
13 | const val ALIPAY_PACKAGE_NAME = "com.eg.android.AlipayGphone"
14 | const val ALIPAY_QRCODE_URI_PREFIX = "alipayqr://platformapi/startapp?saId=10000007&qrcode="
15 | const val ALIPAY_QRCODE_URL = "HTTPS://QR.ALIPAY.COM/FKX074142EKXD0OIMV8B60"
16 | // Alipay end
17 |
18 | // QQ begin
19 | const val QQ_GROUP_KEY = "bJYxW0EzBLB-3NeX1DBuOBxq9sSXnxN4"
20 | // QQ end
21 |
22 | // Taichi begin
23 | const val TAICHI_PACKAGE_NAME = "me.weishu.exp"
24 | const val TAICHI_MAIN_PAGE = "me.weishu.exp.ui.MainActivity"
25 | // Taichi end
26 |
27 | // Xposed Installer begin
28 | const val XPOSED_PACKAGE = "de.robv.android.xposed.installer"
29 |
30 | // Old Xposed installer
31 | const val XPOSED_OPEN_SECTION_ACTION = "$XPOSED_PACKAGE.OPEN_SECTION"
32 | const val XPOSED_EXTRA_SECTION = "section"
33 |
34 | // New Xposed installer
35 | const val XPOSED_ACTIVITY = "$XPOSED_PACKAGE.WelcomeActivity"
36 | const val XPOSED_EXTRA_FRAGMENT = "fragment"
37 | // Xposed Installer end
38 |
39 | // MIUI Weather begin
40 | const val MIUI_WEATHER_PACKAGE = "com.miui.weather2"
41 | // MIUI Weather end
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/cons/PrefConst.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.cons
2 |
3 | import com.tianma.tweaks.miui.BuildConfig
4 |
5 | object PrefConst {
6 | const val SHARED_PREFS_NAME = BuildConfig.APPLICATION_ID + "_preferences"
7 |
8 | // General Settings
9 | const val MAIN_SWITCH = "main_switch"
10 | const val HIDE_LAUNCHER_ICON = "hide_launcher_icon"
11 | // General Settings End
12 |
13 | // StatusBar Settings
14 | const val SHOW_SEC_IN_STATUS_BAR = "show_sec_in_status_bar"
15 | const val STATUS_BAR_CLOCK_ALIGNMENT = "status_bar_clock_alignment"
16 | const val ALIGNMENT_LEFT = "left"
17 | const val ALIGNMENT_CENTER = "center"
18 | const val ALIGNMENT_RIGHT = "right"
19 | const val STATUS_BAR_CLOCK_COLOR_ENABLE = "status_bar_clock_color_enable"
20 | const val STATUS_BAR_CLOCK_COLOR = "status_bar_clock_color"
21 | const val STATUS_BAR_CLOCK_FORMAT_ENABLE = "status_bar_clock_format_enable"
22 | const val STATUS_BAR_CLOCK_FORMAT = "status_bar_clock_format"
23 | const val STATUS_BAR_CLOCK_FORMAT_DEFAULT = "HH:mm:ss"
24 | const val STATUS_BAR_SIGNAL_ALIGN_LEFT = "status_bar_signal_align_left"
25 | const val STATUS_BAR_DUAL_MOBILE_SIGNAL = "status_bar_dual_mobile_signal"
26 | const val STATUS_BAR_HIDE_VPN_ICON = "status_bar_hide_vpn_icon"
27 | const val STATUS_BAR_HIDE_HD_ICON = "status_bar_hide_hd_icon"
28 | const val STATUS_BAR_SHOW_SMALL_BATTERY_PERCENT_SIGN = "status_bar_show_small_battery_percent_sign"
29 | const val CUSTOM_MOBILE_NETWORK_TYPE_ENABLE = "custom_mobile_network_type_enable"
30 | const val CUSTOM_MOBILE_NETWORK_TYPE = "custom_mobile_network_type"
31 | const val CUSTOM_MOBILE_NETWORK_TYPE_DEFAULT = "5G"
32 | const val ALWAYS_SHOW_STATUS_BAR_CLOCK = "always_show_status_bar_clock"
33 | // StatusBar Settings End
34 |
35 | // Dropdown StatusBar Settings
36 | const val SHOW_SEC_IN_DROPDOWN_STATUS_BAR = "show_sec_in_dropdown_status_bar"
37 | const val DROPDOWN_STATUS_BAR_CLOCK_COLOR_ENABLE = "dropdown_status_bar_clock_color_enable"
38 | const val DROPDOWN_STATUS_BAR_CLOCK_COLOR = "dropdown_status_bar_clock_color"
39 | const val DROPDOWN_STATUS_BAR_DATE_COLOR = "dropdown_status_bar_date_color"
40 | const val DROPDOWN_STATUS_BAR_WEATHER_ENABLE = "dropdown_status_bar_weather_enable"
41 | const val DROPDOWN_STATUS_BAR_WEATHER_TEXT_COLOR = "dropdown_status_bar_weather_text_color"
42 | const val DROPDOWN_STATUS_BAR_WEATHER_TEXT_SIZE = "dropdown_status_bar_weather_text_size"
43 | const val DROPDOWN_STATUS_BAR_WEATHER_TEXT_SIZE_DEFAULT = "14"
44 | // Dropdown StatusBar Settings End
45 |
46 | // Keyguard Settings
47 | const val SHOW_SEC_IN_KEYGUARD_HORIZONTAL = "show_sec_in_keyguard_horizontal"
48 | const val SHOW_SEC_IN_KEYGUARD_VERTICAL = "show_sec_in_keyguard_vertical"
49 | const val KEYGUARD_CLOCK_COLOR = "keyguard_clock_color"
50 | // Keyguard Settings end
51 |
52 | // About Settings
53 | const val APP_VERSION = "app_version"
54 | const val SOURCE_CODE = "source_code"
55 | const val KEY_JOIN_QQ_GROUP = "join_qq_group"
56 | const val DONATE_BY_ALIPAY = "donate_by_alipay"
57 | // About Settings End
58 |
59 | // 一言
60 | const val ONE_SENTENCE_ENABLE = "one_sentence_enable"
61 | const val ONE_SENTENCE_SETTINGS = "one_sentence_settings"
62 | const val ONE_SENTENCE_API_SOURCES = "one_sentence_api_sources"
63 | const val API_SOURCE_HITOKOTO = "source_hitokoto"
64 | const val API_SOURCE_ONE_POEM = "source_one_poem"
65 | const val HITOKOTO_CATEGORIES = "hitokoto_categories"
66 | const val HITOKOTO_CATEGORY_ALL = "all"
67 | const val SHOW_HITOKOTO_SOURCE = "show_hitokoto_source"
68 | const val ONE_POEM_CATEGORIES = "one_poem_categories"
69 | const val ONE_POEM_CATEGORY_ALL = "all"
70 | const val SHOW_POEM_AUTHOR = "show_poem_author"
71 | const val ONE_SENTENCE_REFRESH_RATE = "one_sentence_refresh_rate"
72 | const val ONE_SENTENCE_REFRESH_RATE_DEFAULT = "30"
73 | const val ONE_SENTENCE_COLOR = "one_sentence_color"
74 | const val ONE_SENTENCE_TEXT_SIZE = "one_sentence_text_size"
75 | const val ONE_SENTENCE_TEXT_SIZE_DEFAULT = "14"
76 | // 一言 End
77 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/http/APIConst.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.http
2 |
3 | object APIConst {
4 | /**
5 | * 一言API: https://developer.hitokoto.cn/
6 | */
7 | const val HITOKOTO_BASE_URL = "https://v1.hitokoto.cn/"
8 |
9 | /**
10 | * 诗词API: https://www.jinrishici.com/doc/
11 | */
12 | const val POEM_BASE_URL = "https://v1.jinrishici.com/"
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/http/entity/Hitokoto.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.http.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | /**
6 | * 一言
7 | */
8 | // {
9 | // "id": 4401,
10 | // "hitokoto": "那么难受,那么痛苦,可是 世界这么美丽...让我如何能够忘记!",
11 | // "type": "a",
12 | // "from": "朝花夕誓",
13 | // "creator": "飞龙project",
14 | // "created_at": "1553579805"
15 | // }
16 | data class Hitokoto(
17 | @SerializedName("id")
18 | val id: Int,
19 | @SerializedName("hitokoto")
20 | val content: String?,
21 | @SerializedName("type")
22 | val type: String?,
23 | @SerializedName("from")
24 | val from: String?,
25 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/http/entity/Poem.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.http.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | /**
6 | * 诗
7 | */
8 | data class Poem(
9 | @SerializedName("content")
10 | val content: String?,
11 | @SerializedName("origin")
12 | val origin: String?,
13 | @SerializedName("author")
14 | val author: String?,
15 | @SerializedName("category")
16 | val category: String?
17 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/http/repository/DataRepository.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.http.repository
2 |
3 | import com.tianma.tweaks.miui.data.http.APIConst
4 | import com.tianma.tweaks.miui.data.http.entity.Hitokoto
5 | import com.tianma.tweaks.miui.data.http.entity.Poem
6 | import com.tianma.tweaks.miui.data.http.service.HitokotoService
7 | import com.tianma.tweaks.miui.data.http.service.PoemService
8 | import com.tianma.tweaks.miui.data.http.service.ServiceGenerator
9 | import io.reactivex.Observable
10 |
11 | object DataRepository {
12 |
13 | @JvmStatic
14 | fun getHitokoto(categories: List): Observable {
15 | val hitokotoService = ServiceGenerator.instance
16 | .createService(APIConst.HITOKOTO_BASE_URL, HitokotoService::class.java)
17 | return hitokotoService.getHitokoto(categories)
18 | }
19 |
20 | @JvmStatic
21 | fun getPoem(category: String): Observable {
22 | val poemService = ServiceGenerator.instance
23 | .createService(APIConst.POEM_BASE_URL, PoemService::class.java)
24 | return poemService.getPoem(category)
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/http/service/HitokotoService.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.http.service
2 |
3 | import com.tianma.tweaks.miui.data.http.entity.Hitokoto
4 | import io.reactivex.Observable
5 | import retrofit2.http.GET
6 | import retrofit2.http.Query
7 |
8 | /**
9 | * 一言API
10 | */
11 | interface HitokotoService {
12 | @GET("/")
13 | fun getHitokoto(@Query("c") categories: List): Observable
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/http/service/PoemService.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.http.service
2 |
3 | import com.tianma.tweaks.miui.data.http.entity.Poem
4 | import io.reactivex.Observable
5 | import retrofit2.http.GET
6 | import retrofit2.http.Path
7 |
8 | /**
9 | * 今日诗词 API
10 | */
11 | interface PoemService {
12 | @GET("/{category}")
13 | fun getPoem(@Path("category") category: String): Observable
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/http/service/ServiceGenerator.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.http.service
2 |
3 | import android.util.ArrayMap
4 | import okhttp3.OkHttpClient
5 | import retrofit2.Retrofit
6 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
7 | import retrofit2.converter.gson.GsonConverterFactory
8 | import retrofit2.converter.scalars.ScalarsConverterFactory
9 |
10 | /**
11 | * Retrofit Service Generator
12 | */
13 | class ServiceGenerator private constructor() {
14 | private val mRetrofitMap: ArrayMap = ArrayMap()
15 | private val mOkHttpClient: OkHttpClient = OkHttpClient()
16 |
17 | private object InstanceHolder {
18 | val INSTANCE = ServiceGenerator()
19 | }
20 |
21 | fun createService(baseUrl: String, serviceClass: Class): T {
22 | var retrofit = mRetrofitMap[baseUrl]
23 | if (retrofit == null) {
24 | retrofit = Retrofit.Builder()
25 | .baseUrl(baseUrl)
26 | .client(mOkHttpClient)
27 | .addConverterFactory(ScalarsConverterFactory.create())
28 | .addConverterFactory(GsonConverterFactory.create())
29 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
30 | .build()
31 | mRetrofitMap[baseUrl] = retrofit
32 | }
33 | return retrofit!!.create(serviceClass)
34 | }
35 |
36 | companion object {
37 | val instance: ServiceGenerator
38 | get() = InstanceHolder.INSTANCE
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/data/sp/PreferenceContainer.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.data.sp
2 |
3 | import com.tianma.tweaks.miui.cons.AppConst
4 | import com.tianma.tweaks.miui.cons.PrefConst
5 | import com.tianma.tweaks.miui.utils.prefs.PreferenceDelegate
6 |
7 | /**
8 | * desc: App Preference Container
9 | * date: 2021/10/11
10 | */
11 | object PreferenceContainer {
12 |
13 | /**
14 | * 一言 API 源
15 | */
16 | var oneSentenceApiSources by pref(PrefConst.ONE_SENTENCE_API_SOURCES, setOf())
17 |
18 | /**
19 | * 一言 Hitokoto 类别
20 | */
21 | var hitokotoCategories by pref(PrefConst.HITOKOTO_CATEGORIES, setOf())
22 |
23 | /**
24 | * 今日诗词类别
25 | */
26 | var onePoemCategories by pref(PrefConst.ONE_POEM_CATEGORIES, setOf())
27 |
28 | /**
29 | * 是否显示一言 Hitokoto 来源
30 | */
31 | var showHitokotoSource by pref(PrefConst.SHOW_HITOKOTO_SOURCE, false)
32 |
33 | /**
34 | * 是否显示今日诗词作者
35 | */
36 | var showOnePoemAuthor by pref(PrefConst.SHOW_POEM_AUTHOR, false)
37 |
38 | private fun pref(key: String, defaultValue: T): PreferenceDelegate {
39 | return PreferenceDelegate(
40 | key,
41 | defaultValue,
42 | AppConst.XMI_TOOLS_PREFS_NAME
43 | )
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/ContextUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.content.Context
4 | import android.os.Build
5 | import androidx.annotation.RequiresApi
6 |
7 | object ContextUtils {
8 |
9 | @RequiresApi(api = Build.VERSION_CODES.N)
10 | private fun getProtectedContext(context: Context): Context {
11 | return if (context.isDeviceProtectedStorage) {
12 | context
13 | } else {
14 | context.createDeviceProtectedStorageContext()
15 | }
16 | }
17 |
18 | fun getProtectedContextIfNecessary(context: Context): Context {
19 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
20 | // API >= 24 (Android 7.0+)
21 | // dataDir: /data/user_de/0//
22 | // spDir: /data/user_de/0//shared_prefs/
23 | // spFile: /data/user_de/0//shared_prefs/.xml
24 | getProtectedContext(context)
25 | } else {
26 | // API < 24, there is no data encrypt.
27 | // dataDir: /data/data//
28 | context
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/DebugHelper.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.database.Cursor
4 | import android.net.Uri
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.TextView
8 |
9 | object DebugHelper {
10 | fun printViewTree(rootView: View) {
11 | traverseViewTree(rootView, 0)
12 | }
13 |
14 | private fun traverseViewTree(rootView: View, depth: Int) {
15 | var tmpDepth = depth
16 | print(tmpDepth, rootView)
17 | if (rootView is ViewGroup) {
18 | tmpDepth++
19 | for (i in 0 until rootView.childCount) {
20 | val view = rootView.getChildAt(i)
21 | traverseViewTree(view, tmpDepth)
22 | }
23 | }
24 | }
25 |
26 | private fun print(depth: Int, view: View) {
27 | val sb = StringBuilder()
28 | for (i in 0 until depth) {
29 | sb.append("\t")
30 | }
31 | sb.append("|---")
32 | sb.append(view.javaClass.name)
33 | if (view is TextView) {
34 | sb.append(" (")
35 | .append(view.text)
36 | .append(")")
37 | }
38 | logD("%s", sb.toString())
39 | }
40 |
41 | fun printCursor(uri: Uri, c: Cursor?) {
42 | logD("%s", uri.toString())
43 | if (c == null) {
44 | return
45 | }
46 | val hasNext = c.moveToNext()
47 | if (!hasNext) {
48 | return
49 | }
50 | val columnCount = c.columnCount
51 | val columnNames = c.columnNames
52 | val columnTypes = IntArray(columnCount)
53 | for (i in 0 until columnCount) {
54 | columnTypes[i] = c.getType(i)
55 | }
56 | c.moveToPrevious()
57 | while (c.moveToNext()) {
58 | val sb = StringBuilder()
59 | for (i in 0 until columnCount) {
60 | var value: Any? = null
61 | val columnType = columnTypes[i]
62 | if (columnType == Cursor.FIELD_TYPE_INTEGER) {
63 | c.getInt(i)
64 | }
65 | when (columnType) {
66 | Cursor.FIELD_TYPE_INTEGER -> value = c.getInt(i)
67 | Cursor.FIELD_TYPE_BLOB -> value = c.getBlob(i)
68 | Cursor.FIELD_TYPE_FLOAT -> value = c.getFloat(i)
69 | Cursor.FIELD_TYPE_STRING -> value = c.getString(i)
70 | Cursor.FIELD_TYPE_NULL -> {
71 | }
72 | else -> {
73 | }
74 | }
75 | sb.append(columnNames[i]).append(" = ").append(value).append(", ")
76 | }
77 | sb.append("\n")
78 | logD("%s", sb.toString())
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/ModuleUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | /**
4 | * 当前Xposed模块相关工具类
5 | */
6 | object ModuleUtils {
7 | /**
8 | * 当前模块是否在Xposed Installer中被启用
9 | */
10 | @JvmStatic
11 | fun isModuleActive(): Boolean = getModuleVersion() > -1
12 |
13 | /**
14 | * 返回模块版本
15 | * 注意:该方法被本模块Hook住,返回的值是 BuildConfig.MODULE_VERSION,如果没被Hook则返回-1
16 | */
17 | @JvmStatic
18 | fun getModuleVersion(): Int {
19 | logD("getModuleVersion()")
20 | return -1
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/PackageUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.content.pm.PackageManager
6 | import android.net.Uri
7 | import android.widget.Toast
8 | import androidx.annotation.IntDef
9 | import com.tianma.tweaks.miui.BuildConfig
10 | import com.tianma.tweaks.miui.R
11 | import com.tianma.tweaks.miui.cons.AppConst
12 | import com.tianma.tweaks.miui.xp.hook.systemui.SystemUIHook
13 |
14 | /**
15 | * 包相关工具类
16 | */
17 | object PackageUtils {
18 | /**
19 | * not installed
20 | */
21 | const val PACKAGE_NOT_INSTALLED = 0
22 |
23 | /**
24 | * installed & disabled
25 | */
26 | const val PACKAGE_DISABLED = 1
27 |
28 | /**
29 | * installed & enabled
30 | */
31 | const val PACKAGE_ENABLED = 2
32 |
33 | @IntDef(PACKAGE_NOT_INSTALLED, PACKAGE_DISABLED, PACKAGE_ENABLED)
34 | annotation class PackageState
35 |
36 | @JvmStatic
37 | @PackageState
38 | fun checkPackageState(context: Context, packageName: String?): Int {
39 | return if (isPackageEnabled(context, packageName)) {
40 | // installed & enabled
41 | PACKAGE_ENABLED
42 | } else {
43 | if (isPackageInstalled(context, packageName)) {
44 | // installed & disabled
45 | PACKAGE_DISABLED
46 | } else {
47 | // not installed
48 | PACKAGE_NOT_INSTALLED
49 | }
50 | }
51 | }
52 |
53 | /**
54 | * 指定的包名对应的App是否已安装
55 | */
56 | fun isPackageInstalled(context: Context, packageName: String?): Boolean {
57 | val pm = context.packageManager
58 | try {
59 | val packageInfo = pm.getPackageInfo(packageName, 0)
60 | return packageInfo != null
61 | } catch (e: PackageManager.NameNotFoundException) {
62 | // ignore
63 | }
64 | return false
65 | }
66 |
67 | /**
68 | * 对应包名的应用是否已启用
69 | */
70 | fun isPackageEnabled(context: Context, packageName: String?): Boolean {
71 | val pm = context.packageManager
72 | try {
73 | val appInfo = pm.getApplicationInfo(packageName, 0)
74 | return appInfo != null && appInfo.enabled
75 | } catch (e: PackageManager.NameNotFoundException) {
76 | // ignore
77 | }
78 | return false
79 | }
80 |
81 | private fun checkAlipayExists(context: Context): Boolean {
82 | when (checkPackageState(context, AppConst.ALIPAY_PACKAGE_NAME)) {
83 | PACKAGE_ENABLED -> {
84 | return true
85 | }
86 | PACKAGE_DISABLED -> {
87 | Toast.makeText(context, R.string.alipay_enable_prompt, Toast.LENGTH_SHORT).show()
88 | }
89 | PACKAGE_NOT_INSTALLED -> {
90 | Toast.makeText(context, R.string.alipay_install_prompt, Toast.LENGTH_SHORT).show()
91 | }
92 | }
93 | return false
94 | }
95 |
96 | /**
97 | * 打开支付宝捐赠页
98 | */
99 | fun startAlipayDonatePage(context: Context?) {
100 | context ?: return
101 |
102 | if (checkAlipayExists(context)) {
103 | val intent = Intent(Intent.ACTION_VIEW)
104 | intent.data =
105 | Uri.parse(AppConst.ALIPAY_QRCODE_URI_PREFIX + AppConst.ALIPAY_QRCODE_URL)
106 | context.startActivity(intent)
107 | }
108 | }
109 |
110 | private fun checkTaiChiExists(context: Context): Boolean {
111 | when (checkPackageState(context, AppConst.TAICHI_PACKAGE_NAME)) {
112 | PACKAGE_ENABLED -> {
113 | // installed & enabled
114 | return true
115 | }
116 | PACKAGE_NOT_INSTALLED -> {
117 | Toast.makeText(context, R.string.taichi_install_prompt, Toast.LENGTH_SHORT).show()
118 | }
119 | PACKAGE_DISABLED -> {
120 | Toast.makeText(context, R.string.taichi_enable_prompt, Toast.LENGTH_SHORT).show()
121 | }
122 | }
123 | return false
124 | }
125 |
126 | /**
127 | * 请求太极勾选本模块
128 | */
129 | fun startCheckModuleInTaiChi(context: Context) {
130 | if (checkTaiChiExists(context)) {
131 | val intent = Intent("me.weishu.exp.ACTION_MODULE_MANAGE")
132 | intent.data = Uri.parse("package:" + BuildConfig.APPLICATION_ID)
133 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
134 | context.startActivity(intent)
135 | }
136 | }
137 |
138 | /**
139 | * 在太极中勾选本模块相关的应用
140 | */
141 | fun startAddAppsInTaiChi(context: Context) {
142 | if (checkTaiChiExists(context)) {
143 | val intent = Intent("me.weishu.exp.ACTION_ADD_APP")
144 | val uriStr = "package:" + SystemUIHook.PACKAGE_NAME
145 | // "|" + MiuiLauncherHook.PACKAGE_NAME;
146 | intent.data = Uri.parse(uriStr)
147 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
148 | context.startActivity(intent)
149 | }
150 | }
151 |
152 | /**
153 | * Join QQ group
154 | */
155 | fun joinQQGroup(context: Context?) {
156 | context ?: return
157 |
158 | val key = AppConst.QQ_GROUP_KEY
159 | val intent = Intent()
160 | intent.data =
161 | Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D$key")
162 | // 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面
163 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
164 | try {
165 | context.startActivity(intent)
166 | } catch (e: Exception) {
167 | // 未安装手Q或安装的版本不支持
168 | Toast.makeText(context, R.string.prompt_join_qq_group_failed, Toast.LENGTH_SHORT).show()
169 | }
170 | }
171 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/PreferencesUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import com.tianma.tweaks.miui.cons.PrefConst
6 |
7 | /**
8 | * Common Shared preferences utils.
9 | */
10 | object PreferencesUtils {
11 |
12 | private fun getPreferences(context: Context): SharedPreferences {
13 | return context.getSharedPreferences(
14 | PrefConst.SHARED_PREFS_NAME,
15 | Context.MODE_PRIVATE
16 | )
17 | }
18 |
19 | @JvmStatic
20 | fun contains(context: Context, key: String?): Boolean {
21 | return getPreferences(context).contains(key)
22 | }
23 |
24 | @JvmStatic
25 | fun getString(context: Context, key: String?, defValue: String?): String? {
26 | return getPreferences(context).getString(key, defValue)
27 | }
28 |
29 | @JvmStatic
30 | fun putString(context: Context, key: String?, value: String?) {
31 | getPreferences(context).edit().putString(key, value).apply()
32 | }
33 |
34 | @JvmStatic
35 | fun getBoolean(context: Context, key: String?, defValue: Boolean): Boolean {
36 | return getPreferences(context).getBoolean(key, defValue)
37 | }
38 |
39 | @JvmStatic
40 | fun putBoolean(context: Context, key: String?, value: Boolean) {
41 | getPreferences(context).edit().putBoolean(key, value).apply()
42 | }
43 |
44 | @JvmStatic
45 | fun getInt(context: Context, key: String?, defValue: Int): Int {
46 | return getPreferences(context).getInt(key, defValue)
47 | }
48 |
49 | @JvmStatic
50 | fun putInt(context: Context, key: String?, value: Int) {
51 | getPreferences(context).edit().putInt(key, value).apply()
52 | }
53 |
54 | @JvmStatic
55 | fun getFloat(context: Context, key: String?, defValue: Float): Float {
56 | return getPreferences(context).getFloat(key, defValue)
57 | }
58 |
59 | @JvmStatic
60 | fun putFloat(context: Context, key: String?, value: Float) {
61 | getPreferences(context).edit().putFloat(key, value).apply()
62 | }
63 |
64 | @JvmStatic
65 | fun getLong(context: Context, key: String?, defValue: Long): Long {
66 | return getPreferences(context).getLong(key, defValue)
67 | }
68 |
69 | @JvmStatic
70 | fun putLong(context: Context, key: String?, value: Long) {
71 | getPreferences(context).edit().putLong(key, value).apply()
72 | }
73 |
74 | @JvmStatic
75 | fun putStringSet(context: Context, key: String?, values: Set?) {
76 | getPreferences(context).edit().putStringSet(key, values).apply()
77 | }
78 |
79 | @JvmStatic
80 | fun getStringSet(context: Context, key: String?, defValues: Set): Set? {
81 | return getPreferences(context).getStringSet(key, defValues)
82 | }
83 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/ReflectionExt.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import java.lang.reflect.Field
4 | import java.lang.reflect.Method
5 |
6 | /**
7 | * desc: Extension functions about Xposed.
8 | * date: 2021/8/5
9 | */
10 |
11 | // 获取指定类的 Field (包括父类的 & 非 public 的)
12 | fun Class<*>.getExactFiled(fieldName: String): Field? = try {
13 | getDeclaredField(fieldName)
14 | } catch (e: NoSuchFieldException) {
15 | superclass.getExactFiled(fieldName)
16 | }
17 |
18 | // 获取对象中指定 Field (包括父类的 & 非 public 的) 的值
19 | operator fun Any?.get(propertyName: String): Any? = when {
20 | this == null -> null
21 | this is Class<*> -> {
22 | try {
23 | this.getExactFiled(propertyName)?.apply {
24 | isAccessible = true
25 | }?.get(null)
26 | } catch (e: NoSuchFieldException) {
27 | null
28 | }
29 | }
30 | else -> {
31 | try {
32 | this::class.java.getExactFiled(propertyName)?.apply {
33 | isAccessible = true
34 | }?.get(this)
35 | } catch (e: NoSuchFieldException) {
36 | null
37 | }
38 | }
39 | }
40 |
41 | // 获取指定类中的指定方法 (包含父类的 & 非 public 的)
42 | fun Class<*>.getExactMethod(
43 | methodName: String,
44 | parameterTypes: Array> = arrayOf(),
45 | ): Method? {
46 | return try {
47 | ReflectionUtils.getDeclaredMethod(this, methodName, *parameterTypes)
48 | } catch (e: NoSuchMethodException) {
49 | superclass.getExactMethod(methodName, parameterTypes)
50 | }
51 | }
52 |
53 | // 调用指定类or对象的指定方法 (包含父类方法 & 非 public 方法)
54 | fun Any?.callMethod(
55 | methodName: String,
56 | parameterTypes: Array> = arrayOf(),
57 | args: Array = arrayOf()
58 | ): Any? {
59 | return when {
60 | this == null -> null
61 | this is Class<*> -> {
62 | try {
63 | val method = this.getExactMethod(methodName, parameterTypes)
64 | ReflectionUtils.invoke(method, null, args)
65 | } catch (e: NoSuchMethodException) {
66 | logE("", e)
67 | null
68 | }
69 | }
70 | else -> {
71 | try {
72 | val method = this::class.java.getExactMethod(methodName, parameterTypes)
73 | ReflectionUtils.invoke(method, this, *args)
74 | } catch (e: NoSuchMethodException) {
75 | logE("", e)
76 | null
77 | }
78 | }
79 | }
80 | }
81 |
82 |
83 | fun Any?.safeAs(): T? {
84 | return this as? T
85 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/ReflectionUtils.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils;
2 |
3 | import java.lang.reflect.Field;
4 | import java.lang.reflect.InvocationTargetException;
5 | import java.lang.reflect.Method;
6 |
7 | /**
8 | * Utils for reflection
9 | */
10 | public final class ReflectionUtils {
11 | private ReflectionUtils() {
12 | }
13 |
14 | public static Class> getClass(ClassLoader classLoader, String name) {
15 | try {
16 | return Class.forName(name, true, classLoader);
17 | } catch (ClassNotFoundException e) {
18 | throw new RuntimeException(e);
19 | }
20 | }
21 |
22 | public static Field getDeclaredField(Class> cls, String fieldName) {
23 | Field field;
24 | try {
25 | field = cls.getDeclaredField(fieldName);
26 | } catch (NoSuchFieldException e) {
27 | throw new RuntimeException(e);
28 | }
29 | field.setAccessible(true);
30 | return field;
31 | }
32 |
33 | public static Field getField(Class> cls, String fieldName) {
34 | Field field;
35 | try {
36 | field = cls.getField(fieldName);
37 | } catch (NoSuchFieldException e) {
38 | throw new RuntimeException(e);
39 | }
40 | field.setAccessible(true);
41 | return field;
42 | }
43 |
44 | public static Object getFieldValue(Field field, Object object) {
45 | try {
46 | return field.get(object);
47 | } catch (IllegalAccessException e) {
48 | throw new RuntimeException(e);
49 | }
50 | }
51 |
52 | public static void setFieldValue(Field field, Object object, Object value) {
53 | try {
54 | field.set(object, value);
55 | } catch (IllegalAccessException e) {
56 | throw new RuntimeException(e);
57 | }
58 | }
59 |
60 | public static Method getDeclaredMethod(Class> cls, String methodName, Class>... paramTypes) throws NoSuchMethodException {
61 | Method method = cls.getDeclaredMethod(methodName, paramTypes);
62 | method.setAccessible(true);
63 | return method;
64 | }
65 |
66 | public static Method getMethod(Class> cls, String methodName, Class>... paramTypes) throws NoSuchMethodException {
67 | Method method = cls.getMethod(methodName, paramTypes);
68 | method.setAccessible(true);
69 | return method;
70 | }
71 |
72 | public static Object invoke(Method method, Object thisObject, Object... params) {
73 | try {
74 | return method.invoke(thisObject, params);
75 | } catch (InvocationTargetException e) {
76 | Throwable cause = e.getCause();
77 | if (cause instanceof RuntimeException) {
78 | throw (RuntimeException) cause;
79 | } else {
80 | throw new RuntimeException(e);
81 | }
82 | } catch (IllegalAccessException e) {
83 | throw new RuntimeException(e);
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/ResolutionUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.content.Context
4 | import android.util.DisplayMetrics
5 | import com.tianma.tweaks.miui.utils.ResolutionUtils
6 | import android.view.WindowManager
7 |
8 | object ResolutionUtils {
9 | private fun getDisplayMetrics(context: Context): DisplayMetrics {
10 | return context.resources.displayMetrics
11 | }
12 |
13 | @JvmStatic
14 | fun dp2px(context: Context, dp: Float): Float {
15 | return dp * getDisplayMetrics(context).density
16 | }
17 |
18 | fun px2dp(context: Context, px: Float): Float {
19 | return px / getDisplayMetrics(context).density
20 | }
21 |
22 | fun getScreenWidth(context: Context): Int {
23 | val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager?
24 | val outMetrics = DisplayMetrics()
25 | return if (wm != null) {
26 | wm.defaultDisplay.getMetrics(outMetrics)
27 | outMetrics.widthPixels
28 | } else {
29 | 0
30 | }
31 | }
32 |
33 | fun getScreenHeight(context: Context): Int {
34 | val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager?
35 | val outMetrics = DisplayMetrics()
36 | return if (wm != null) {
37 | wm.defaultDisplay.getMetrics(outMetrics)
38 | outMetrics.heightPixels
39 | } else {
40 | 0
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/RootUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import com.jaredrummler.android.shell.Shell
4 | import com.tianma.tweaks.miui.utils.RootUtils
5 | import com.tianma.tweaks.miui.xp.hook.systemui.SystemUIHook
6 | import com.tianma.tweaks.miui.xp.hook.launcher.MiuiLauncherHook
7 |
8 | /**
9 | * Utils for root action
10 | */
11 | object RootUtils {
12 | /**
13 | * Restart SystemUI
14 | */
15 | fun restartSystemUI() {
16 | killAll(SystemUIHook.PACKAGE_NAME)
17 | }
18 |
19 | /**
20 | * Kill Miui Launcher
21 | */
22 | fun killAllMiuiLauncher() {
23 | killAll(MiuiLauncherHook.PACKAGE_NAME)
24 | }
25 |
26 | /**
27 | * killall
28 | *
29 | * @param processName process name
30 | */
31 | private fun killAll(processName: String) {
32 | val cmd = String.format("killall %s", processName)
33 | Shell.SU.run(cmd)
34 | }
35 |
36 | /**
37 | * Reboot
38 | */
39 | fun reboot() {
40 | Shell.SU.run("reboot")
41 | }
42 |
43 | /**
44 | * Soft Reboot
45 | */
46 | fun softReboot() {
47 | Shell.SU.run("setprop ctl.restart surfaceflinger; setprop ctl.restart zygote")
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/SPUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.content.Context
4 | import com.tianma.tweaks.miui.utils.PreferencesUtils.getLong
5 | import com.tianma.tweaks.miui.utils.PreferencesUtils.putLong
6 |
7 | object SPUtils {
8 | private const val ONE_SENTENCE_LAST_REFRESH_TIME = "one_sentence_last_refresh_time"
9 |
10 | // 存储上次刷新时间
11 | @JvmStatic
12 | fun setOneSentenceLastRefreshTime(context: Context, timestamp: Long) {
13 | putLong(context, ONE_SENTENCE_LAST_REFRESH_TIME, timestamp)
14 | }
15 |
16 | // 获取上次刷新时间
17 | @JvmStatic
18 | fun getOneSentenceLastRefreshTime(context: Context): Long {
19 | return getLong(context, ONE_SENTENCE_LAST_REFRESH_TIME, 0L)
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/StorageUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.os.Environment
6 | import androidx.core.content.ContextCompat
7 | import com.tianma.tweaks.miui.BuildConfig
8 | import java.io.File
9 |
10 | /**
11 | * Utils for storage.
12 | */
13 | object StorageUtils {
14 |
15 | /**
16 | * 是否有SD卡
17 | */
18 | @JvmStatic
19 | fun isSDCardMounted(): Boolean {
20 | val state = Environment.getExternalStorageState()
21 | return Environment.MEDIA_MOUNTED == state
22 | }
23 |
24 | /**
25 | * Get sdcard directory
26 | *
27 | * @return SD card directory
28 | */
29 | @JvmStatic
30 | fun getSDCardDir(): File = Environment.getExternalStorageDirectory()
31 |
32 | /**
33 | * get sdcard public documents directory
34 | *
35 | * @return SD card public documents directory
36 | */
37 | @JvmStatic
38 | fun getPublicDocumentsDir(): File =
39 | Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
40 |
41 |
42 | @JvmStatic
43 | fun getSharedPreferencesFile(context: Context?, preferencesName: String): File {
44 | val dataDir = ContextCompat.getDataDir(context!!)
45 | val prefsDir = File(dataDir, "shared_prefs")
46 | return File(prefsDir, "$preferencesName.xml")
47 | }
48 |
49 | /**
50 | * Get internal data dir. /data/data//
51 | * */
52 | @JvmStatic
53 | fun getInternalDataDir(): File {
54 | return File(Environment.getDataDirectory(), "data/" + BuildConfig.APPLICATION_ID)
55 | }
56 |
57 | /**
58 | * Get internal files dir. /data/data//files/
59 | *
60 | * @return
61 | * */
62 | @JvmStatic
63 | fun getInternalFilesDir(): File {
64 | return File(getInternalDataDir(), "files")
65 | }
66 |
67 | /**
68 | * Get external files dir. /sdcard/Android/data//files/
69 | * */
70 | @JvmStatic
71 | fun getExternalFilesDir(): File {
72 | return File(
73 | Environment.getExternalStorageDirectory(),
74 | "Android/data/" + BuildConfig.APPLICATION_ID + "/files/"
75 | )
76 | }
77 |
78 | /**
79 | * Get files dir
80 | *
81 | * @see StorageUtils.getExternalFilesDir
82 | * @see StorageUtils.getInternalFilesDir
83 | */
84 | @JvmStatic
85 | fun getFilesDir(): File {
86 | return if (isSDCardMounted()) {
87 | val externalFilesDir = getExternalFilesDir()
88 | if (!externalFilesDir.exists()) {
89 | externalFilesDir.mkdirs()
90 | }
91 | externalFilesDir
92 | } else {
93 | getInternalFilesDir()
94 | }
95 | }
96 |
97 | /**
98 | * Set file world writable
99 | */
100 | @JvmStatic
101 | @SuppressLint("SetWorldWritable", "SetWorldReadable")
102 | fun setFileWorldWritable(file: File?, parentDepth: Int) {
103 | file ?: return
104 | if (!file.exists()) {
105 | return
106 | }
107 | var tempFile: File? = file
108 | val tempDepth = parentDepth + 1
109 | for (i in 0 until tempDepth) {
110 | tempFile?.setExecutable(true, false)
111 | tempFile?.setWritable(true, false)
112 | tempFile?.setReadable(true, false)
113 | tempFile = tempFile?.parentFile
114 | if (tempFile == null) {
115 | break
116 | }
117 | }
118 | }
119 |
120 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import androidx.browser.customtabs.CustomTabsIntent
6 | import android.widget.Toast
7 | import com.tianma.tweaks.miui.R
8 | import java.lang.Exception
9 |
10 | /**
11 | * Other Utils
12 | */
13 | object Utils {
14 | fun showWebPage(context: Context?, url: String?) {
15 | try {
16 | val cti = CustomTabsIntent.Builder().build()
17 | cti.launchUrl(context!!, Uri.parse(url))
18 | } catch (e: Exception) {
19 | Toast.makeText(context, R.string.browser_install_or_enable_prompt, Toast.LENGTH_SHORT)
20 | .show()
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/XLog.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils
2 |
3 | import android.util.Log
4 | import com.tianma.tweaks.miui.BuildConfig
5 |
6 | /**
7 | * desc: Log util
8 | * date: 2021/8/6
9 | */
10 |
11 | private const val LOG_TAG: String = BuildConfig.LOG_TAG
12 | private const val LOG_LEVEL = BuildConfig.LOG_LEVEL
13 | private const val LOG_TO_XPOSED = BuildConfig.LOG_TO_XPOSED
14 | private const val LOG_TO_EDXPOSED = BuildConfig.LOG_TO_EDXPOSED
15 |
16 | private fun log(priority: Int, message: String, vararg args: Any) {
17 |
18 | var targetPriority = priority
19 | var msg = message
20 | if (targetPriority < LOG_LEVEL) return
21 |
22 | if (args.isNotEmpty()) {
23 | msg = String.format(msg, *args)
24 | }
25 |
26 | if (args.isNotEmpty() && args[args.size - 1] is Throwable) {
27 | val throwable = args[args.size - 1] as Throwable
28 | val stacktraceStr = Log.getStackTraceString(throwable)
29 | msg += "\n${stacktraceStr}"
30 | }
31 |
32 | // Write to the default log tag
33 | Log.println(targetPriority, LOG_TAG, msg)
34 |
35 | // Duplicate to the Xposed log if enabled
36 | if (LOG_TO_XPOSED) {
37 | if (targetPriority <= Log.DEBUG) { // DEBUG level 不会在 Xposed 日志中生成,所以调整等级
38 | targetPriority = Log.INFO
39 | }
40 | Log.println(targetPriority, "Xposed", "$LOG_TAG: $msg")
41 | }
42 | if (LOG_TO_EDXPOSED) {
43 | if (targetPriority <= Log.DEBUG) { // DEBUG level 不会在 EdXposed 日志中生成,所以调整等级
44 | targetPriority = Log.INFO
45 | }
46 | // EdXposed
47 | Log.println(targetPriority, "EdXposed-Bridge", "$LOG_TAG: $msg")
48 | // LSPosed
49 | Log.println(targetPriority, "LSPosed-Bridge", "$LOG_TAG: $msg")
50 | }
51 | }
52 |
53 | fun logV(message: String, vararg args: Any) {
54 | log(Log.VERBOSE, message, *args)
55 | }
56 |
57 | fun logD(message: String, vararg args: Any) {
58 | log(Log.DEBUG, message, *args)
59 | }
60 |
61 | fun logI(message: String, vararg args: Any) {
62 | log(Log.INFO, message, *args)
63 | }
64 |
65 | fun logW(message: String, vararg args: Any) {
66 | log(Log.WARN, message, *args)
67 | }
68 |
69 | fun logE(message: String, vararg args: Any) {
70 | log(Log.ERROR, message, *args)
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/prefs/PreferenceDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils.prefs
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import androidx.core.content.edit
6 | import com.tianma.tweaks.miui.app.App
7 | import com.tianma.tweaks.miui.utils.ContextUtils
8 | import com.tianma.tweaks.miui.utils.safeAs
9 | import kotlin.properties.ReadWriteProperty
10 | import kotlin.reflect.KProperty
11 |
12 | /**
13 | * desc: Preference 的属性代理
14 | * date: 6/7/21
15 | */
16 | class PreferenceDelegate(
17 | private val key: String,
18 | private val defaultValue: T,
19 | private val prefName: String = "default"
20 | ) : ReadWriteProperty {
21 |
22 | private val sharedPrefs: SharedPreferences by lazy {
23 | val context = ContextUtils.getProtectedContextIfNecessary(App.appContext)
24 | context.getSharedPreferences(prefName, Context.MODE_PRIVATE)
25 | }
26 |
27 | override fun getValue(thisRef: Any?, property: KProperty<*>): T {
28 | return findPreference(findPreferenceKey(property))
29 | }
30 |
31 | private fun findPreferenceKey(property: KProperty<*>): String {
32 | return if (key.isEmpty()) {
33 | property.name
34 | } else {
35 | key
36 | }
37 | }
38 |
39 | @Suppress("UNCHECKED_CAST")
40 | private fun findPreference(key: String): T {
41 | return when (defaultValue) {
42 | is Int -> sharedPrefs.getInt(key, defaultValue)
43 | is String -> sharedPrefs.getString(key, defaultValue)
44 | is Boolean -> sharedPrefs.getBoolean(key, defaultValue)
45 | is Long -> sharedPrefs.getLong(key, defaultValue)
46 | is Float -> sharedPrefs.getFloat(key, defaultValue)
47 | is Set<*> -> sharedPrefs.getStringSet(key, defaultValue.safeAs()).safeAs()
48 | else -> throw IllegalArgumentException("Unsupported type. $key $defaultValue")
49 | } as T
50 | }
51 |
52 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
53 | putPreference(findPreferenceKey(property), value)
54 | }
55 |
56 | private fun putPreference(key: String, value: T) {
57 | sharedPrefs.edit {
58 | when (value) {
59 | is Long -> putLong(key, value)
60 | is Int -> putInt(key, value)
61 | is Boolean -> putBoolean(key, value)
62 | is String -> putString(key, value)
63 | is Float -> putFloat(key, value)
64 | is Set<*> -> putStringSet(key, value.safeAs())
65 | else -> throw IllegalArgumentException("Unsupported type. $key $defaultValue")
66 | }
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/prefs/XPreferenceDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils.prefs
2 |
3 | import android.os.Build
4 | import androidx.core.content.edit
5 | import com.tianma.tweaks.miui.utils.logD
6 | import com.tianma.tweaks.miui.utils.logE
7 | import com.tianma.tweaks.miui.utils.safeAs
8 | import de.robv.android.xposed.XSharedPreferences
9 | import java.io.File
10 | import kotlin.properties.ReadWriteProperty
11 | import kotlin.reflect.KProperty
12 |
13 | /**
14 | * desc: XSharedPreference 的属性代理
15 | * date: 6/7/21
16 | */
17 | class XPreferenceDelegate(
18 | private val key: String,
19 | private val defaultValue: T,
20 | private val packageName: String,
21 | private val prefFileName: String
22 | ) : ReadWriteProperty {
23 |
24 | private val sharedPrefs: XSharedPreferences by lazy {
25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // Android 7.0+
26 | val prefFile = File("/data/user_de/0/${packageName}/shared_prefs/${prefFileName}.xml")
27 | XSharedPreferences(prefFile)
28 | } else {
29 | XSharedPreferences(packageName, prefFileName)
30 | }.also { xsp ->
31 | try {
32 | xsp.makeWorldReadable()
33 | } catch (t: Throwable) {
34 | logE("", t)
35 | }
36 | }
37 | }
38 |
39 | override fun getValue(thisRef: Any?, property: KProperty<*>): T {
40 | return findPreference(findPreferenceKey(property))
41 | }
42 |
43 | private fun findPreferenceKey(property: KProperty<*>): String {
44 | return if (key.isEmpty()) {
45 | property.name
46 | } else {
47 | key
48 | }
49 | }
50 |
51 | @Suppress("UNCHECKED_CAST")
52 | private fun findPreference(key: String): T {
53 | return (when (defaultValue) {
54 | is Int -> sharedPrefs.getInt(key, defaultValue)
55 | is String -> sharedPrefs.getString(key, defaultValue)
56 | is Boolean -> sharedPrefs.getBoolean(key, defaultValue)
57 | is Long -> sharedPrefs.getLong(key, defaultValue)
58 | is Float -> sharedPrefs.getFloat(key, defaultValue)
59 | is Set<*> -> sharedPrefs.getStringSet(key, defaultValue.safeAs()).safeAs()
60 | else -> throw IllegalArgumentException("Unsupported type. $key $defaultValue")
61 | } as T).also {
62 | }
63 | }
64 |
65 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
66 | putPreference(findPreferenceKey(property), value)
67 | }
68 |
69 | private fun putPreference(key: String, value: T) {
70 | sharedPrefs.edit {
71 | when (value) {
72 | is Long -> putLong(key, value)
73 | is Int -> putInt(key, value)
74 | is Boolean -> putBoolean(key, value)
75 | is String -> putString(key, value)
76 | is Float -> putFloat(key, value)
77 | is Set<*> -> putStringSet(key, value.safeAs())
78 | else -> throw IllegalArgumentException("Unsupported type. $key $defaultValue")
79 | }
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/rom/MiuiUtils.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils.rom;
2 |
3 | import android.os.Build;
4 | import android.text.TextUtils;
5 |
6 | import com.tianma.tweaks.miui.utils.XLogKt;
7 |
8 | /**
9 | * MIUI Rom 相关工具类
10 | */
11 | public class MiuiUtils {
12 |
13 | private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
14 | private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
15 | private static final String KEY_VERSION_CODE_TIME = "ro.miui.version.code_time";
16 |
17 | private MiuiUtils() {
18 | }
19 |
20 | /**
21 | * 判断当前Rom是否是MIUI
22 | */
23 | public static boolean isMiui() {
24 | return !TextUtils.isEmpty(getMiuiVersionName()) || getMiuiVersionCode() != -1;
25 | }
26 |
27 | /**
28 | * 获取MIUI版本名(v10, v9 之类)
29 | */
30 | public static String getMiuiVersionName() {
31 | return RomUtils.getSystemProperty(KEY_MIUI_VERSION_NAME);
32 | }
33 |
34 | /**
35 | * 获取 MIUI 大版本,MIUI v10 返回 10, MIUI v9 返回 9, ro.miui.ui.version.code 键不存在,否则返回 -1
36 | */
37 | public static int getMiuiVersionCode() {
38 | String versionCode = RomUtils.getSystemProperty(KEY_MIUI_VERSION_CODE);
39 | if (!TextUtils.isEmpty(versionCode)) {
40 | try {
41 | return Integer.parseInt(versionCode) + 2;
42 | } catch (Exception e) {
43 | XLogKt.logE("get MIUI version code failed: %s", versionCode, e);
44 | }
45 | }
46 | return -1;
47 | }
48 |
49 | /**
50 | * 获取 MIUI 小版本,比如 9.5.9, 8.12.27 之类的,代表构建日期
51 | * @return
52 | */
53 | public static String getMiuiVersionIncremental() {
54 | return Build.VERSION.INCREMENTAL;
55 | }
56 |
57 | /**
58 | * 获取当前MIUI版本构建时间(单位ms)
59 | * @return
60 | */
61 | private static long getMiuiVersionCodeTime() {
62 | String versionCodeTime = RomUtils.getSystemProperty(KEY_VERSION_CODE_TIME);
63 | if (!TextUtils.isEmpty(versionCodeTime)) {
64 | try {
65 | return Long.parseLong(versionCodeTime) * 1000;
66 | } catch (Exception e) {
67 | XLogKt.logE("get MIUI version code time failed: %s", versionCodeTime, e);
68 | }
69 | }
70 | return 0L;
71 | }
72 |
73 | public static MiuiVersion getMiuiVersion() {
74 | return new MiuiVersion(getMiuiVersionCodeTime());
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/rom/MiuiVersion.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils.rom;
2 |
3 | import com.tianma.tweaks.miui.utils.XLogKt;
4 |
5 | import java.text.SimpleDateFormat;
6 | import java.util.Date;
7 | import java.util.Locale;
8 |
9 | public class MiuiVersion {
10 |
11 | public final static MiuiVersion V_19_5_7 = new MiuiVersion("19.5.7");
12 |
13 | private static final String DATE_FORMAT = "yy.M.d";
14 |
15 | private long time;
16 |
17 | public MiuiVersion(String timeStr) {
18 | SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault());
19 | try {
20 | time = sdf.parse(timeStr).getTime();
21 | } catch (Exception e) {
22 | XLogKt.logE("time format error %s", timeStr, e);
23 | }
24 | }
25 |
26 | public MiuiVersion(long time) {
27 | this.time = time;
28 | }
29 |
30 | public long getTime() {
31 | return time;
32 | }
33 |
34 | @Override
35 | public String toString() {
36 | return "MiuiVersion{" +
37 | "time=" + new SimpleDateFormat(DATE_FORMAT, Locale.getDefault()).format(new Date(time)) +
38 | '}';
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/utils/rom/RomUtils.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.utils.rom;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 |
7 | /**
8 | * Rom 相关工具类
9 | */
10 | public class RomUtils {
11 |
12 | private RomUtils() {
13 | }
14 |
15 | public static String getSystemProperty(String propertyName) {
16 | String result = null;
17 | BufferedReader br = null;
18 | try {
19 | Process process = Runtime.getRuntime().exec("getprop " + propertyName);
20 | br = new BufferedReader(new InputStreamReader(process.getInputStream()), 1024);
21 | result = br.readLine();
22 | } catch (Exception e) {
23 | e.printStackTrace();
24 | } finally {
25 | if (br != null) {
26 | try {
27 | br.close();
28 | } catch (IOException e) {
29 | e.printStackTrace();
30 | }
31 | }
32 | }
33 | return result;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/HookEntry.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp
2 |
3 | import com.tianma.tweaks.miui.xp.hook.BaseHook
4 | import com.tianma.tweaks.miui.xp.hook.self.ModuleUtilsHook
5 | import com.tianma.tweaks.miui.xp.hook.systemui.SystemUIHook
6 | import de.robv.android.xposed.IXposedHookLoadPackage
7 | import de.robv.android.xposed.IXposedHookZygoteInit
8 | import de.robv.android.xposed.IXposedHookZygoteInit.StartupParam
9 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
10 |
11 | class HookEntry : IXposedHookLoadPackage, IXposedHookZygoteInit {
12 |
13 | private val hookList: List by lazy {
14 | mutableListOf().apply {
15 | add(ModuleUtilsHook()) // Self Hook
16 | add(SystemUIHook()) // SystemUI Hook
17 | // add(new MiuiLauncherHook()); // Miui Launcher Hook
18 | }
19 | }
20 |
21 | @Throws(Throwable::class)
22 | override fun initZygote(startupParam: StartupParam?) {
23 | startupParam ?: return
24 | for (hook in hookList) {
25 | if (hook.shouldHookInitZygote()) {
26 | hook.initZygote(startupParam)
27 | }
28 | }
29 | }
30 |
31 | @Throws(Throwable::class)
32 | override fun handleLoadPackage(lpparam: LoadPackageParam?) {
33 | lpparam ?: return
34 | for (hook in hookList) {
35 | if (hook.shouldHookOnLoadPackage()) {
36 | hook.onLoadPackage(lpparam)
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/BaseHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook
2 |
3 | import de.robv.android.xposed.IXposedHookZygoteInit.StartupParam
4 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
5 |
6 | open class BaseHook : IHook {
7 | @Throws(Throwable::class)
8 | override fun initZygote(startupParam: StartupParam) {
9 | }
10 |
11 | open fun shouldHookInitZygote(): Boolean {
12 | return false
13 | }
14 |
15 | @Throws(Throwable::class)
16 | override fun onLoadPackage(lpparam: LoadPackageParam) {
17 | }
18 |
19 | open fun shouldHookOnLoadPackage(): Boolean {
20 | return true
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/BaseSubHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook
2 |
3 | import kotlin.jvm.JvmOverloads
4 | import com.tianma.tweaks.miui.utils.rom.MiuiVersion
5 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
6 |
7 | abstract class BaseSubHook @JvmOverloads constructor(
8 | val mClassLoader: ClassLoader?,
9 | val mAppInfo: AppInfo? = null,
10 | val mMiuiVersion: MiuiVersion? = null
11 | ) {
12 |
13 | abstract fun startHook()
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/IHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook
2 |
3 | import kotlin.Throws
4 | import de.robv.android.xposed.IXposedHookZygoteInit.StartupParam
5 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
6 |
7 | interface IHook {
8 | @Throws(Throwable::class)
9 | fun initZygote(startupParam: StartupParam)
10 |
11 | @Throws(Throwable::class)
12 | fun onLoadPackage(lpparam: LoadPackageParam)
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/launcher/MiuiLauncherHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.launcher
2 |
3 | import com.tianma.tweaks.miui.data.sp.XPrefContainer.mainSwitchEnable
4 | import com.tianma.tweaks.miui.utils.logI
5 | import com.tianma.tweaks.miui.utils.rom.MiuiUtils
6 | import com.tianma.tweaks.miui.xp.hook.BaseHook
7 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
8 |
9 | class MiuiLauncherHook : BaseHook() {
10 |
11 | companion object {
12 | const val PACKAGE_NAME = "com.miui.home"
13 | }
14 |
15 | @Throws(Throwable::class)
16 | override fun onLoadPackage(lpparam: LoadPackageParam) {
17 | if (PACKAGE_NAME == lpparam.packageName) {
18 | logI("Hooking MIUI Launcher...")
19 | val classLoader = lpparam.classLoader
20 | if (mainSwitchEnable) {
21 | if (!MiuiUtils.isMiui()) {
22 | return
23 | }
24 | WorkSpaceHook(classLoader).startHook()
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/launcher/WorkSpaceHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.launcher
2 |
3 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
4 | import com.tianma.tweaks.miui.utils.logD
5 | import com.tianma.tweaks.miui.utils.logE
6 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
7 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
8 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
9 |
10 | class WorkSpaceHook(classLoader: ClassLoader?) : BaseSubHook(classLoader) {
11 |
12 | companion object {
13 | private const val CLASS_WORK_SPACE = "com.miui.home.launcher.Workspace"
14 | }
15 |
16 | private val mAlwaysShowStatusBarClock: Boolean = XPrefContainer.alwaysShowStatusBarClock
17 |
18 | override fun startHook() {
19 | try {
20 | logD("Hooking WorkSpace...")
21 | if (mAlwaysShowStatusBarClock) {
22 | hookIsScreenHasClockGadgets()
23 | }
24 | } catch (t: Throwable) {
25 | logE("Error occurs when hook WorkSpace", t)
26 | }
27 | }
28 |
29 | // #isScreenHasClockGadget()
30 | private fun hookIsScreenHasClockGadgets() {
31 | XposedWrapper.findAndHookMethod(
32 | CLASS_WORK_SPACE,
33 | mClassLoader,
34 | "isScreenHasClockGadget",
35 | Long::class.javaPrimitiveType,
36 | object : MethodHookWrapper() {
37 | override fun before(param: MethodHookParam) {
38 | param.result = false
39 | }
40 | })
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/self/ModuleUtilsHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.self
2 |
3 | import com.tianma.tweaks.miui.BuildConfig
4 | import com.tianma.tweaks.miui.utils.ModuleUtils
5 | import com.tianma.tweaks.miui.utils.logE
6 | import com.tianma.tweaks.miui.utils.logI
7 | import com.tianma.tweaks.miui.xp.hook.BaseHook
8 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
9 | import de.robv.android.xposed.XC_MethodReplacement
10 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
11 |
12 | /**
13 | * Hook class ModuleUtils
14 | */
15 | class ModuleUtilsHook : BaseHook() {
16 |
17 | companion object {
18 | private const val MI_TWEAKS_PACKAGE = BuildConfig.APPLICATION_ID
19 | private const val MODULE_VERSION = BuildConfig.MODULE_VERSION
20 | }
21 |
22 | @Throws(Throwable::class)
23 | override fun onLoadPackage(lpparam: LoadPackageParam) {
24 | if (MI_TWEAKS_PACKAGE == lpparam.packageName) {
25 | try {
26 | logI("Hooking current Xposed module status...")
27 | hookModuleUtils(lpparam)
28 | } catch (e: Throwable) {
29 | logE("Failed to hook current Xposed module status.")
30 | }
31 | }
32 | }
33 |
34 | @Throws(Throwable::class)
35 | private fun hookModuleUtils(lpparam: LoadPackageParam) {
36 | val className = ModuleUtils::class.java.name
37 | XposedWrapper.findAndHookMethod(
38 | className, lpparam.classLoader,
39 | "getModuleVersion",
40 | XC_MethodReplacement.returnConstant(MODULE_VERSION)
41 | )
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/helper/ResHelpers.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.helper;
2 |
3 | import android.content.res.Resources;
4 | import android.util.ArrayMap;
5 |
6 | import com.tianma.tweaks.miui.xp.hook.systemui.SystemUIHook;
7 |
8 | public class ResHelpers {
9 |
10 | private static ArrayMap sNameIdMap;
11 |
12 | static {
13 | sNameIdMap = new ArrayMap<>();
14 | }
15 |
16 | public static Integer getId(Resources res, String name) {
17 | if (!sNameIdMap.containsKey(name)) {
18 | int id = res.getIdentifier(name, "id", SystemUIHook.PACKAGE_NAME);
19 | sNameIdMap.put(name, id);
20 | }
21 | return sNameIdMap.get(name);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/keyguard/def/Ease.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.keyguard.def;
2 |
3 | import android.animation.TimeInterpolator;
4 |
5 | public class Ease {
6 |
7 | public static class Cubic {
8 | public static final TimeInterpolator easeIn = new TimeInterpolator() {
9 | public float getInterpolation(float f) {
10 | float f2 = f / 1.0f;
11 | f = f2;
12 | return (((1.0f * f2) * f) * f) + 0.0f;
13 | }
14 | };
15 | public static final TimeInterpolator easeInOut = new TimeInterpolator() {
16 | public float getInterpolation(float f) {
17 | float f2 = f / 0.5f;
18 | f = f2;
19 | if (f2 < 1.0f) {
20 | return (((0.5f * f) * f) * f) + 0.0f;
21 | }
22 | float f3 = f - 2.0f;
23 | f = f3;
24 | return (0.5f * (((f3 * f) * f) + 2.0f)) + 0.0f;
25 | }
26 | };
27 | public static final TimeInterpolator easeOut = new TimeInterpolator() {
28 | public float getInterpolation(float f) {
29 | float f2 = (f / 1.0f) - 1.0f;
30 | f = f2;
31 | return (1.0f * (((f2 * f) * f) + 1.0f)) + 0.0f;
32 | }
33 | };
34 | }
35 |
36 | public static class Quint {
37 | public static final TimeInterpolator easeIn = new TimeInterpolator() {
38 | public float getInterpolation(float f) {
39 | float f2 = f / 1.0f;
40 | f = f2;
41 | return (((((1.0f * f2) * f) * f) * f) * f) + 0.0f;
42 | }
43 | };
44 | public static final TimeInterpolator easeInOut = new TimeInterpolator() {
45 | public float getInterpolation(float f) {
46 | float f2 = f / 0.5f;
47 | f = f2;
48 | if (f2 < 1.0f) {
49 | return (((((0.5f * f) * f) * f) * f) * f) + 0.0f;
50 | }
51 | float f3 = f - 2.0f;
52 | f = f3;
53 | return (0.5f * (((((f3 * f) * f) * f) * f) + 2.0f)) + 0.0f;
54 | }
55 | };
56 | public static final TimeInterpolator easeOut = new TimeInterpolator() {
57 | public float getInterpolation(float f) {
58 | float f2 = (f / 1.0f) - 1.0f;
59 | f = f2;
60 | return (1.0f * (((((f2 * f) * f) * f) * f) + 1.0f)) + 0.0f;
61 | }
62 | };
63 | }
64 |
65 | public static class Sine {
66 | public static final TimeInterpolator easeIn = new TimeInterpolator() {
67 | public float getInterpolation(float f) {
68 | return ((-1.0f * ((float) Math.cos(((double) (f / 1.0f)) * 1.5707963267948966d))) + 1.0f) + 0.0f;
69 | }
70 | };
71 | public static final TimeInterpolator easeInOut = new TimeInterpolator() {
72 | public float getInterpolation(float f) {
73 | return (-0.5f * (((float) Math.cos((3.141592653589793d * ((double) f)) / 1.0d)) - 1.0f)) + 0.0f;
74 | }
75 | };
76 | public static final TimeInterpolator easeOut = new TimeInterpolator() {
77 | public float getInterpolation(float f) {
78 | return (1.0f * ((float) Math.sin(((double) (f / 1.0f)) * 1.5707963267948966d))) + 0.0f;
79 | }
80 | };
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/keyguard/def/KeyguardClockContainerHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.keyguard.def
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.IntentFilter
5 | import android.os.UserHandle
6 | import android.widget.FrameLayout
7 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
8 | import com.tianma.tweaks.miui.utils.logD
9 | import com.tianma.tweaks.miui.utils.logE
10 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
11 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
12 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppVersionConst
13 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
14 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
15 | import de.robv.android.xposed.XposedHelpers
16 |
17 | /**
18 | * 锁屏时钟容器 Hook
19 | * 适用版本 9.4.x
20 | */
21 | class KeyguardClockContainerHook(classLoader: ClassLoader?, appInfo: AppInfo?) :
22 | BaseSubHook(classLoader, appInfo) {
23 |
24 | companion object {
25 | private const val CLASS_KEYGUARD_CLOCK_CONTAINER_OLD =
26 | "com.android.keyguard.KeyguardClockContainer"
27 |
28 | private const val CLASS_KEYGUARD_CLOCK_CONTAINER_NEW =
29 | "com.android.keyguard.clock.KeyguardClockContainer"
30 |
31 | private const val CLASS_DEPENDENCY = "com.android.systemui.Dependency"
32 | }
33 |
34 | private val mShowHorizontalSec: Boolean = XPrefContainer.showSecInKeyguardHorizontal
35 | private val mShowVerticalSec: Boolean = XPrefContainer.showSecInKeyguardVertical
36 |
37 | private var mKeyguardClockContainerClass: Class<*>? = null
38 |
39 | override fun startHook() {
40 | if (!mShowHorizontalSec && !mShowVerticalSec) {
41 | return
42 | }
43 | try {
44 | logD("Hooking KeyguardClockContainerHook...")
45 | mKeyguardClockContainerClass =
46 | if (mAppInfo!!.versionCode >= AppVersionConst.SYSTEM_UI_V201912130) {
47 | XposedHelpers.findClass(CLASS_KEYGUARD_CLOCK_CONTAINER_NEW, mClassLoader)
48 | } else {
49 | XposedHelpers.findClass(CLASS_KEYGUARD_CLOCK_CONTAINER_OLD, mClassLoader)
50 | }
51 | hookOnAttachedToWindow()
52 | } catch (t: Throwable) {
53 | logE("Error occurs when hook KeyguardClockContainerHook", t)
54 | }
55 | }
56 |
57 | // com.android.keyguard.KeyguardClockContainer#onAttachedToWindow()
58 | private fun hookOnAttachedToWindow() {
59 | XposedWrapper.findAndHookMethod(mKeyguardClockContainerClass,
60 | "onAttachedToWindow",
61 | object : MethodHookWrapper() {
62 | override fun before(param: MethodHookParam) {
63 | val clockContainer = param.thisObject as FrameLayout
64 | val parent = clockContainer.parent
65 | XposedHelpers.callMethod(parent, "onAttachedToWindow")
66 |
67 | val filter = IntentFilter()
68 | // 目的: 取消注册 TIME_TICK 事件
69 | // filter.addAction("android.intent.action.TIME_TICK");
70 | filter.addAction("android.intent.action.TIME_SET")
71 | filter.addAction("android.intent.action.TIMEZONE_CHANGED")
72 |
73 | val mIntentReceiver = XposedHelpers.getObjectField(
74 | clockContainer,
75 | "mIntentReceiver"
76 | ) as BroadcastReceiver
77 |
78 | val userHandleAll = XposedHelpers.getStaticObjectField(UserHandle::class.java, "ALL")
79 | val dependencyClass = XposedHelpers.findClass(CLASS_DEPENDENCY, mClassLoader)
80 | val timeTickHandler = XposedHelpers.getStaticObjectField(dependencyClass, "TIME_TICK_HANDLER")
81 | val handler =
82 | XposedHelpers.callStaticMethod(dependencyClass, "get", timeTickHandler)
83 |
84 | XposedHelpers.callMethod(
85 | clockContainer.context,
86 | "registerReceiverAsUser",
87 | mIntentReceiver,
88 | userHandleAll,
89 | filter,
90 | null,
91 | handler
92 | )
93 |
94 | XposedHelpers.callMethod(clockContainer, "registerDualClockObserver")
95 | XposedHelpers.callMethod(clockContainer, "registerClockPositionObserver")
96 |
97 | param.result = null
98 | }
99 | })
100 | }
101 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/keyguard/v20190507/ChooseKeyguardClockActivityHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.keyguard.v20190507
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
6 | import com.tianma.tweaks.miui.utils.logD
7 | import com.tianma.tweaks.miui.utils.logE
8 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
9 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.IntentAction
10 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
11 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
12 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
13 |
14 | /**
15 | * MIUI设置页面 - 选择锁屏时钟界面 Hook
16 | * 适用版本 9.5.7+
17 | */
18 | class ChooseKeyguardClockActivityHook(classLoader: ClassLoader?, appInfo: AppInfo?) :
19 | BaseSubHook(classLoader, appInfo) {
20 |
21 | companion object {
22 | private const val CLASS_CHOOSE_KEYGUARD_CLOCK_ACTIVITY =
23 | "com.android.keyguard.settings.ChooseKeyguardClockActivity"
24 | }
25 |
26 | private val mShowVerticalSec: Boolean = XPrefContainer.showSecInKeyguardVertical
27 | private val mShowHorizontalSec: Boolean = XPrefContainer.showSecInKeyguardHorizontal
28 |
29 | override fun startHook() {
30 | if (!mShowHorizontalSec && !mShowVerticalSec) {
31 | return
32 | }
33 | try {
34 | logD("Hooking ChooseKeyguardClockActivity...")
35 | hookOnStop()
36 | } catch (t: Throwable) {
37 | logE("Error occurs when hook ChooseKeyguardClockActivity", t)
38 | }
39 | }
40 |
41 | // com.android.keyguard.setting.ChooseKeyguardClockActivity#onStop()
42 | private fun hookOnStop() {
43 | XposedWrapper.findAndHookMethod(
44 | CLASS_CHOOSE_KEYGUARD_CLOCK_ACTIVITY,
45 | mClassLoader,
46 | "onStop",
47 | object : MethodHookWrapper() {
48 | override fun before(param: MethodHookParam) {
49 | val context = param.thisObject as Context
50 | val intent = Intent(IntentAction.KEYGUARD_STOP_TIME_TICK)
51 | context.sendBroadcast(intent)
52 | }
53 | })
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/keyguard/v20191213/MiuiCenterHorizontalClockHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.keyguard.v20191213
2 |
3 | import android.view.View
4 | import android.view.ViewTreeObserver.OnWindowAttachListener
5 | import android.widget.TextView
6 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
7 | import com.tianma.tweaks.miui.utils.logD
8 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
9 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.ScreenBroadcastManager
10 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.SimpleScreenListener
11 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TickObserver
12 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TimeTicker
13 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
14 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
15 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
16 | import de.robv.android.xposed.XposedHelpers
17 | import java.util.*
18 |
19 | /**
20 | * 锁屏居中水平时钟
21 | * 适用版本 20.4.27+
22 | */
23 | class MiuiCenterHorizontalClockHook(classLoader: ClassLoader?, appInfo: AppInfo?) : BaseSubHook(classLoader, appInfo), TickObserver {
24 |
25 | companion object {
26 | const val CLASS_MIUI_CENTER_HORIZONTAL_CLOCK = "miui.keyguard.clock.MiuiCenterHorizontalClock"
27 | }
28 |
29 | private var centerHorizontalClockClass: Class<*>? = null
30 |
31 | private val clockList = mutableListOf()
32 |
33 | private val showHorizontalSec = XPrefContainer.showSecInKeyguardHorizontal
34 |
35 | override fun startHook() {
36 | if (showHorizontalSec) {
37 | logD("Hooking MiuiCenterHorizontalClock...")
38 | centerHorizontalClockClass = XposedWrapper.findClass(CLASS_MIUI_CENTER_HORIZONTAL_CLOCK, mClassLoader)
39 |
40 | centerHorizontalClockClass?.let {
41 | hookConstructor()
42 | hookUpdateTime()
43 | }
44 | }
45 | }
46 |
47 | private fun hookConstructor() {
48 | XposedWrapper.hookAllConstructors(centerHorizontalClockClass,
49 | object : MethodHookWrapper() {
50 | override fun after(param: MethodHookParam?) {
51 | param?.let {
52 | val miuiBaseClock = it.thisObject as View
53 | miuiBaseClock.viewTreeObserver.addOnWindowAttachListener(object : OnWindowAttachListener {
54 | override fun onWindowAttached() {
55 | addClock(miuiBaseClock)
56 | }
57 |
58 | override fun onWindowDetached() {
59 | removeClock(miuiBaseClock)
60 | }
61 | })
62 |
63 | addClock(miuiBaseClock)
64 |
65 | ScreenBroadcastManager.getInstance(miuiBaseClock.context).registerListener(screenListener)
66 | }
67 | }
68 | })
69 | }
70 |
71 | @Synchronized
72 | private fun addClock(clock: View) {
73 | if (!clockList.contains(clock)) {
74 | clockList.add(clock)
75 | val size: Int = clockList.size
76 | val limitedSize = 2
77 | if (size > limitedSize) {
78 | for (i in 0 until size - limitedSize) {
79 | val item: View? = clockList[i]
80 | clockList.remove(item)
81 | }
82 | }
83 | }
84 | if (clockList.isNotEmpty()) {
85 | TimeTicker.get().registerObserver(this)
86 | }
87 | }
88 |
89 | @Synchronized
90 | private fun removeClock(clock: View) {
91 | clockList.remove(clock)
92 | if (clockList.isEmpty()) {
93 | TimeTicker.get().unregisterObserver(this)
94 | }
95 | }
96 |
97 | private val screenListener: SimpleScreenListener = object : SimpleScreenListener() {
98 | override fun onScreenOn() {
99 | TimeTicker.get().registerObserver(this@MiuiCenterHorizontalClockHook)
100 | }
101 |
102 | override fun onScreenOff() {
103 | TimeTicker.get().unregisterObserver(this@MiuiCenterHorizontalClockHook)
104 | }
105 |
106 | override fun onUserPresent() {
107 | TimeTicker.get().unregisterObserver(this@MiuiCenterHorizontalClockHook)
108 | }
109 |
110 | override fun onStopTimeTick() {
111 | TimeTicker.get().unregisterObserver(this@MiuiCenterHorizontalClockHook)
112 | }
113 | }
114 |
115 | override fun onTimeTick() {
116 | for (keyguardClock in clockList) {
117 | if (keyguardClock != null) {
118 | XposedHelpers.callMethod(keyguardClock, "updateTime")
119 | }
120 | }
121 | }
122 |
123 | private fun hookUpdateTime() {
124 | XposedWrapper.findAndHookMethod(centerHorizontalClockClass,
125 | "updateTime",
126 | object : MethodHookWrapper() {
127 | override fun after(param: MethodHookParam?) {
128 | param?.let {
129 | val mTimeText = XposedHelpers.getObjectField(param.thisObject, "mTimeText") as TextView
130 | val originalTimeStr = mTimeText.text.toString()
131 | mTimeText.text = addInSecond(originalTimeStr)
132 | }
133 | }
134 | })
135 | }
136 |
137 | private fun addInSecond(originalTimeStr: String): String? {
138 | val sec = Calendar.getInstance()[Calendar.SECOND]
139 | val secStr = String.format(Locale.getDefault(), "%02d", sec)
140 | return originalTimeStr.replace("(\\d+:\\d+)(:\\d+)?".toRegex(), "$1:$secStr")
141 | }
142 |
143 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/keyguard/v20191213/MiuiLeftTopClockHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.keyguard.v20191213
2 |
3 | import android.view.View
4 | import android.view.ViewTreeObserver.OnWindowAttachListener
5 | import android.widget.TextView
6 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
7 | import com.tianma.tweaks.miui.utils.logD
8 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
9 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.ScreenBroadcastManager
10 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.SimpleScreenListener
11 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TickObserver
12 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TimeTicker
13 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
14 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
15 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
16 | import de.robv.android.xposed.XposedHelpers
17 | import java.util.*
18 |
19 | /**
20 | * 锁屏左上角小时钟
21 | * 适用版本 20.4.27+
22 | */
23 | class MiuiLeftTopClockHook(classLoader: ClassLoader?, appInfo: AppInfo?) : BaseSubHook(classLoader, appInfo), TickObserver {
24 |
25 | companion object {
26 | const val CLASS_MIUI_LEFT_TOP_CLOCK = "miui.keyguard.clock.MiuiLeftTopClock"
27 | }
28 |
29 | private var leftTopClockClass: Class<*>? = null
30 |
31 | private val clockList = mutableListOf()
32 |
33 | private val showHorizontalSec = XPrefContainer.showSecInKeyguardHorizontal
34 |
35 | override fun startHook() {
36 | if (showHorizontalSec) {
37 | logD("Hooking MiuiLeftTopClock...")
38 | leftTopClockClass = XposedWrapper.findClass(CLASS_MIUI_LEFT_TOP_CLOCK, mClassLoader)
39 | leftTopClockClass?.let {
40 | hookConstructor()
41 | hookUpdateTime()
42 | }
43 | }
44 | }
45 |
46 | private fun hookConstructor() {
47 | XposedWrapper.hookAllConstructors(leftTopClockClass,
48 | object : MethodHookWrapper() {
49 | override fun after(param: MethodHookParam?) {
50 | param?.let {
51 | val miuiBaseClock = it.thisObject as View
52 | miuiBaseClock.viewTreeObserver.addOnWindowAttachListener(object : OnWindowAttachListener {
53 | override fun onWindowAttached() {
54 | addClock(miuiBaseClock)
55 | }
56 |
57 | override fun onWindowDetached() {
58 | removeClock(miuiBaseClock)
59 | }
60 | })
61 |
62 | addClock(miuiBaseClock)
63 |
64 | ScreenBroadcastManager.getInstance(miuiBaseClock.context).registerListener(screenListener)
65 | }
66 | }
67 | })
68 | }
69 |
70 | @Synchronized
71 | private fun addClock(clock: View) {
72 | if (!clockList.contains(clock)) {
73 | clockList.add(clock)
74 | val size: Int = clockList.size
75 | val limitedSize = 2
76 | if (size > limitedSize) {
77 | for (i in 0 until size - limitedSize) {
78 | val item: View? = clockList[i]
79 | clockList.remove(item)
80 | }
81 | }
82 | }
83 | if (clockList.isNotEmpty()) {
84 | TimeTicker.get().registerObserver(this@MiuiLeftTopClockHook)
85 | }
86 | }
87 |
88 | @Synchronized
89 | private fun removeClock(clock: View) {
90 | clockList.remove(clock)
91 | if (clockList.isEmpty()) {
92 | TimeTicker.get().unregisterObserver(this@MiuiLeftTopClockHook)
93 | }
94 | }
95 |
96 | private val screenListener: SimpleScreenListener = object : SimpleScreenListener() {
97 | override fun onScreenOn() {
98 | TimeTicker.get().registerObserver(this@MiuiLeftTopClockHook)
99 | }
100 |
101 | override fun onScreenOff() {
102 | TimeTicker.get().unregisterObserver(this@MiuiLeftTopClockHook)
103 | }
104 |
105 | override fun onUserPresent() {
106 | TimeTicker.get().unregisterObserver(this@MiuiLeftTopClockHook)
107 | }
108 |
109 | override fun onStopTimeTick() {
110 | TimeTicker.get().unregisterObserver(this@MiuiLeftTopClockHook)
111 | }
112 | }
113 |
114 | override fun onTimeTick() {
115 | for (keyguardClock in clockList) {
116 | if (keyguardClock != null) {
117 | XposedHelpers.callMethod(keyguardClock, "updateTime")
118 | }
119 | }
120 | }
121 |
122 | private fun hookUpdateTime() {
123 | XposedWrapper.findAndHookMethod(leftTopClockClass,
124 | "updateTime",
125 | object : MethodHookWrapper() {
126 | override fun after(param: MethodHookParam?) {
127 | param?.let {
128 | val mTimeText = XposedHelpers.getObjectField(param.thisObject, "mTimeText") as TextView
129 | val originalTimeStr = mTimeText.text.toString()
130 | mTimeText.text = addInSecond(originalTimeStr)
131 | }
132 | }
133 | })
134 | }
135 |
136 | private fun addInSecond(originalTimeStr: String): String? {
137 | val sec = Calendar.getInstance()[Calendar.SECOND]
138 | val secStr = String.format(Locale.getDefault(), "%02d", sec)
139 | return originalTimeStr.replace("(\\d+:\\d+)(:\\d+)?".toRegex(), "$1:$secStr")
140 | }
141 |
142 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/keyguard/v20191213/MiuiLeftTopLargeClockHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.keyguard.v20191213
2 |
3 | import android.view.View
4 | import android.view.ViewTreeObserver.OnWindowAttachListener
5 | import android.widget.TextView
6 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
7 | import com.tianma.tweaks.miui.utils.logD
8 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
9 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.ScreenBroadcastManager
10 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.SimpleScreenListener
11 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TickObserver
12 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TimeTicker
13 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
14 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
15 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
16 | import de.robv.android.xposed.XposedHelpers
17 | import java.util.*
18 |
19 | /**
20 | * 锁屏左上角大时钟
21 | * 适用版本 20.4.27+
22 | */
23 | class MiuiLeftTopLargeClockHook(classLoader: ClassLoader?, appInfo: AppInfo?) : BaseSubHook(classLoader, appInfo), TickObserver {
24 |
25 | companion object {
26 | const val CLASS_MIUI_LEFT_TOP_LARGE_CLOCK = "miui.keyguard.clock.MiuiLeftTopLargeClock"
27 | }
28 |
29 | private var leftTopLargeClockClass: Class<*>? = null
30 |
31 | private val clockList = mutableListOf()
32 |
33 | // private val showHorizontalSec = XSPUtils.showSecInKeyguardHorizontal(xsp)
34 | private val showHorizontalSec = XPrefContainer.showSecInKeyguardHorizontal
35 |
36 | override fun startHook() {
37 | if (showHorizontalSec) {
38 | logD("Hooking MiuiLeftTopLargeClock...")
39 | leftTopLargeClockClass = XposedWrapper.findClass(CLASS_MIUI_LEFT_TOP_LARGE_CLOCK, mClassLoader)
40 |
41 | leftTopLargeClockClass?.let {
42 | hookConstructor()
43 | hookUpdateTime()
44 | }
45 | }
46 | }
47 |
48 | private fun hookConstructor() {
49 | XposedWrapper.hookAllConstructors(leftTopLargeClockClass,
50 | object : MethodHookWrapper() {
51 | override fun after(param: MethodHookParam?) {
52 | param?.let {
53 | val miuiBaseClock = it.thisObject as View
54 | miuiBaseClock.viewTreeObserver.addOnWindowAttachListener(object : OnWindowAttachListener {
55 | override fun onWindowAttached() {
56 | addClock(miuiBaseClock)
57 | }
58 |
59 | override fun onWindowDetached() {
60 | removeClock(miuiBaseClock)
61 | }
62 | })
63 |
64 | addClock(miuiBaseClock)
65 |
66 | ScreenBroadcastManager.getInstance(miuiBaseClock.context).registerListener(screenListener)
67 | }
68 | }
69 | })
70 | }
71 |
72 | @Synchronized
73 | private fun addClock(clock: View) {
74 | if (!clockList.contains(clock)) {
75 | clockList.add(clock)
76 | val size: Int = clockList.size
77 | val limitedSize = 2
78 | if (size > limitedSize) {
79 | for (i in 0 until size - limitedSize) {
80 | val item: View? = clockList[i]
81 | clockList.remove(item)
82 | }
83 | }
84 | }
85 | if (clockList.isNotEmpty()) {
86 | TimeTicker.get().registerObserver(this)
87 | }
88 | }
89 |
90 | @Synchronized
91 | private fun removeClock(clock: View) {
92 | clockList.remove(clock)
93 | if (clockList.isEmpty()) {
94 | TimeTicker.get().unregisterObserver(this)
95 | }
96 | }
97 |
98 | private val screenListener: SimpleScreenListener = object : SimpleScreenListener() {
99 | override fun onScreenOn() {
100 | TimeTicker.get().registerObserver(this@MiuiLeftTopLargeClockHook)
101 | }
102 |
103 | override fun onScreenOff() {
104 | TimeTicker.get().unregisterObserver(this@MiuiLeftTopLargeClockHook)
105 | }
106 |
107 | override fun onUserPresent() {
108 | TimeTicker.get().unregisterObserver(this@MiuiLeftTopLargeClockHook)
109 | }
110 |
111 | override fun onStopTimeTick() {
112 | TimeTicker.get().unregisterObserver(this@MiuiLeftTopLargeClockHook)
113 | }
114 | }
115 |
116 | override fun onTimeTick() {
117 | for (keyguardClock in clockList) {
118 | if (keyguardClock != null) {
119 | XposedHelpers.callMethod(keyguardClock, "updateTime")
120 | }
121 | }
122 | }
123 |
124 | private fun hookUpdateTime() {
125 | XposedWrapper.findAndHookMethod(leftTopLargeClockClass,
126 | "updateTime",
127 | object : MethodHookWrapper() {
128 | override fun after(param: MethodHookParam?) {
129 | param?.let {
130 | val mTimeText = XposedHelpers.getObjectField(param.thisObject, "mTimeText") as TextView
131 | val originalTimeStr = mTimeText.text.toString()
132 | mTimeText.text = addInSecond(originalTimeStr)
133 | }
134 | }
135 | })
136 | }
137 |
138 | private fun addInSecond(originalTimeStr: String): String? {
139 | val sec = Calendar.getInstance()[Calendar.SECOND]
140 | val secStr = String.format(Locale.getDefault(), "%02d", sec)
141 | return originalTimeStr.replace("(\\d+:\\d+)(:\\d+)?".toRegex(), "$1:$secStr")
142 | }
143 |
144 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/keyguard/v20191213/MiuiVerticalClockHook.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.keyguard.v20191213
2 |
3 | import android.view.View
4 | import android.view.ViewTreeObserver.OnWindowAttachListener
5 | import android.widget.TextView
6 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
7 | import com.tianma.tweaks.miui.utils.logD
8 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
9 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.ScreenBroadcastManager
10 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.SimpleScreenListener
11 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TickObserver
12 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TimeTicker
13 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
14 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
15 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
16 | import de.robv.android.xposed.XposedHelpers
17 | import java.util.*
18 |
19 | /**
20 | * 锁屏居中垂直时钟
21 | * 适用版本 20.4.27+
22 | */
23 | class MiuiVerticalClockHook(classLoader: ClassLoader?, appInfo: AppInfo?) : BaseSubHook(classLoader, appInfo), TickObserver {
24 |
25 | companion object {
26 | const val CLASS_MIUI_VERTICAL_CLOCK = "miui.keyguard.clock.MiuiVerticalClock"
27 | }
28 |
29 | private var verticalClockClass: Class<*>? = null
30 |
31 | private val clockList = mutableListOf()
32 |
33 | // private var showVerticalSec = XSPUtils.showSecInKeyguardVertical(xsp)
34 | private var showVerticalSec = XPrefContainer.showSecInKeyguardVertical
35 |
36 | override fun startHook() {
37 | if(showVerticalSec) {
38 | logD("Hooking MiuiVerticalClock...")
39 | verticalClockClass = XposedWrapper.findClass(CLASS_MIUI_VERTICAL_CLOCK, mClassLoader)
40 | verticalClockClass?.let {
41 | hookConstructor()
42 | hookUpdateTime()
43 | }
44 | }
45 | }
46 |
47 | private fun hookConstructor() {
48 | XposedWrapper.hookAllConstructors(verticalClockClass,
49 | object : MethodHookWrapper() {
50 | override fun after(param: MethodHookParam?) {
51 | param?.let {
52 | val miuiBaseClock = it.thisObject as View
53 | miuiBaseClock.viewTreeObserver.addOnWindowAttachListener(object : OnWindowAttachListener {
54 | override fun onWindowAttached() {
55 | addClock(miuiBaseClock)
56 | }
57 |
58 | override fun onWindowDetached() {
59 | removeClock(miuiBaseClock)
60 | }
61 | })
62 |
63 | addClock(miuiBaseClock)
64 |
65 | ScreenBroadcastManager.getInstance(miuiBaseClock.context).registerListener(screenListener)
66 | }
67 | }
68 | })
69 | }
70 |
71 | @Synchronized
72 | private fun addClock(clock: View) {
73 | if (!clockList.contains(clock)) {
74 | clockList.add(clock)
75 | val size: Int = clockList.size
76 | val limitedSize = 2
77 | if (size > limitedSize) {
78 | for (i in 0 until size - limitedSize) {
79 | val item: View? = clockList[i]
80 | clockList.remove(item)
81 | }
82 | }
83 | }
84 | if (clockList.isNotEmpty()) {
85 | TimeTicker.get().registerObserver(this@MiuiVerticalClockHook)
86 | }
87 | }
88 |
89 | @Synchronized
90 | private fun removeClock(clock: View) {
91 | clockList.remove(clock)
92 | if (clockList.isEmpty()) {
93 | TimeTicker.get().unregisterObserver(this@MiuiVerticalClockHook)
94 | }
95 | }
96 |
97 | private val screenListener: SimpleScreenListener = object : SimpleScreenListener() {
98 | override fun onScreenOn() {
99 | TimeTicker.get().registerObserver(this@MiuiVerticalClockHook)
100 | }
101 |
102 | override fun onScreenOff() {
103 | TimeTicker.get().unregisterObserver(this@MiuiVerticalClockHook)
104 | }
105 |
106 | override fun onUserPresent() {
107 | TimeTicker.get().unregisterObserver(this@MiuiVerticalClockHook)
108 | }
109 |
110 | override fun onStopTimeTick() {
111 | TimeTicker.get().unregisterObserver(this@MiuiVerticalClockHook)
112 | }
113 | }
114 |
115 | override fun onTimeTick() {
116 | for (keyguardClock in clockList) {
117 | if (keyguardClock != null) {
118 | XposedHelpers.callMethod(keyguardClock, "updateTime")
119 | }
120 | }
121 | }
122 |
123 | private fun hookUpdateTime() {
124 | XposedWrapper.findAndHookMethod(verticalClockClass,
125 | "updateTime",
126 | object : MethodHookWrapper() {
127 | override fun after(param: MethodHookParam?) {
128 | param?.let {
129 | val mTimeText = XposedHelpers.getObjectField(param.thisObject, "mTimeText") as TextView
130 | val originalTimeStr = mTimeText.text.toString()
131 | mTimeText.text = addInSecond(originalTimeStr)
132 | }
133 | }
134 | })
135 | }
136 |
137 | private fun addInSecond(originalTimeStr: String): String? {
138 | val sec = Calendar.getInstance()[Calendar.SECOND]
139 | val secStr = String.format(Locale.getDefault(), "%02d", sec)
140 | return originalTimeStr.replace("(\\d+\n\\d+)(\n\\d+)?".toRegex(), "$1\n$secStr")
141 | }
142 |
143 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/screen/IntentAction.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.screen
2 |
3 | object IntentAction {
4 | const val KEYGUARD_STOP_TIME_TICK = "com.tianma.tweaks.miui.KEYGUARD_STOP_TIME_TICK"
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/screen/ScreenBroadcastManager.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.screen
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.IntentFilter
7 | import com.tianma.tweaks.miui.xp.hook.systemui.tick.TimeTicker
8 | import java.lang.ref.WeakReference
9 | import java.util.*
10 |
11 | /**
12 | * Screen Broadcast Manager
13 | */
14 | class ScreenBroadcastManager private constructor(context: Context) {
15 |
16 | private val listeners: MutableList = ArrayList()
17 | private val contextWeakReference: WeakReference =
18 | WeakReference(context.applicationContext)
19 |
20 | companion object {
21 | @Volatile
22 | private var sInstance: ScreenBroadcastManager? = null
23 |
24 | @JvmStatic
25 | fun getInstance(modContext: Context): ScreenBroadcastManager {
26 | return sInstance ?: synchronized(TimeTicker::class.java) {
27 | sInstance ?: ScreenBroadcastManager(modContext).also {
28 | sInstance = it
29 | }
30 | }
31 | }
32 | }
33 |
34 | @Synchronized
35 | fun registerListener(screenListener: ScreenListener) {
36 | if (listeners.isEmpty()) {
37 | registerBroadcastReceiver()
38 | }
39 | if (!listeners.contains(screenListener)) {
40 | listeners.add(screenListener)
41 | }
42 | }
43 |
44 | @Synchronized
45 | fun unregisterListener(screenListener: ScreenListener) {
46 | listeners.remove(screenListener)
47 | if (listeners.isEmpty()) {
48 | unregisterBroadcastReceiver()
49 | }
50 | }
51 |
52 | // register broadcast receiver for screen on, off, user-present, stop-time-tick
53 | private fun registerBroadcastReceiver() {
54 | val context = contextWeakReference.get()
55 | if (context != null) {
56 | // register receiver
57 | val filter = IntentFilter()
58 | filter.addAction(Intent.ACTION_SCREEN_ON)
59 | filter.addAction(Intent.ACTION_USER_PRESENT)
60 | filter.addAction(Intent.ACTION_SCREEN_OFF)
61 | filter.addAction(IntentAction.KEYGUARD_STOP_TIME_TICK)
62 | context.registerReceiver(screenReceiver, filter)
63 | }
64 | }
65 |
66 | // unregister broadcast receiver
67 | private fun unregisterBroadcastReceiver() {
68 | val context = contextWeakReference.get()
69 | context?.unregisterReceiver(screenReceiver)
70 | }
71 |
72 | // screen receiver
73 | private val screenReceiver: BroadcastReceiver by lazy {
74 | object : BroadcastReceiver() {
75 | override fun onReceive(context: Context, intent: Intent) {
76 | val action = intent.action
77 | notifyListeners(action)
78 | }
79 | }
80 | }
81 |
82 | // notify screen event to all listeners (ScreenListener)
83 | private fun notifyListeners(action: String?) {
84 | val screenOn = Intent.ACTION_SCREEN_ON == action
85 | val userPresent = Intent.ACTION_USER_PRESENT == action
86 | val screenOff = Intent.ACTION_SCREEN_OFF == action
87 | val stopTimeTick = IntentAction.KEYGUARD_STOP_TIME_TICK == action
88 |
89 | for (listener in listeners) {
90 | when {
91 | screenOn -> {
92 | listener.onScreenOn()
93 | }
94 | userPresent -> {
95 | listener.onUserPresent()
96 | }
97 | screenOff -> {
98 | listener.onScreenOff()
99 | }
100 | stopTimeTick -> {
101 | listener.onStopTimeTick()
102 | }
103 | }
104 | }
105 | }
106 |
107 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/screen/ScreenListener.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.screen
2 |
3 | /**
4 | * Screen Listener
5 | */
6 | interface ScreenListener {
7 | /**
8 | * called when screen on
9 | */
10 | fun onScreenOn()
11 |
12 | /**
13 | * called when screen off
14 | */
15 | fun onScreenOff()
16 |
17 | /**
18 | * called when user present
19 | */
20 | fun onUserPresent()
21 |
22 | /**
23 | * called when stop time tick
24 | */
25 | fun onStopTimeTick()
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/screen/SimpleScreenListener.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.screen
2 |
3 | open class SimpleScreenListener : ScreenListener {
4 | override fun onScreenOn() {}
5 | override fun onScreenOff() {}
6 | override fun onUserPresent() {}
7 | override fun onStopTimeTick() {}
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/statusbar/def/BatteryMeterViewHook.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.statusbar.def;
2 |
3 | import android.text.SpannableString;
4 | import android.text.Spanned;
5 | import android.text.style.AbsoluteSizeSpan;
6 | import android.widget.TextView;
7 |
8 | import com.tianma.tweaks.miui.data.sp.XPrefContainer;
9 | import com.tianma.tweaks.miui.utils.XLogKt;
10 | import com.tianma.tweaks.miui.utils.rom.MiuiVersion;
11 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook;
12 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper;
13 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper;
14 |
15 | import de.robv.android.xposed.XposedHelpers;
16 |
17 | public class BatteryMeterViewHook extends BaseSubHook {
18 |
19 | private static final String CLASS_BATTERY_VIEW = "com.android.systemui.BatteryMeterView";
20 |
21 | private boolean mShowSmallPercentSign;
22 |
23 | public BatteryMeterViewHook(ClassLoader classLoader, MiuiVersion miuiVersion) {
24 | super(classLoader, null, miuiVersion);
25 |
26 | // mShowSmallPercentSign = XSPUtils.showSmallBatteryPercentSign(xsp);
27 | mShowSmallPercentSign = XPrefContainer.getShowSmallBatteryPercentSign();
28 | }
29 |
30 | @Override
31 | public void startHook() {
32 | XLogKt.logD("Hooking BatteryMeterView...");
33 | if (mShowSmallPercentSign) {
34 | hookUpdateShowPercent();
35 | }
36 | }
37 |
38 | private void hookUpdateShowPercent() {
39 | XposedWrapper.findAndHookMethod(CLASS_BATTERY_VIEW,
40 | getMClassLoader(),
41 | "updateShowPercent",
42 | new MethodHookWrapper() {
43 | @Override
44 | protected void after(MethodHookParam param) {
45 |
46 | TextView mBatteryPercentView = (TextView) XposedHelpers
47 | .getObjectField(param.thisObject, "mBatteryPercentView");
48 | if (mBatteryPercentView != null) {
49 | CharSequence cs = mBatteryPercentView.getText();
50 | if (cs == null) {
51 | return;
52 | }
53 | String text = cs.toString();
54 | int percentSignIdx = text.indexOf('%');
55 | if (percentSignIdx != -1) {
56 | SpannableString ss = new SpannableString(text);
57 | float originSize = mBatteryPercentView.getTextSize();
58 | ss.setSpan(new AbsoluteSizeSpan((int) (originSize * 3 / 4)), percentSignIdx, percentSignIdx + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
59 | mBatteryPercentView.setText(ss);
60 | }
61 | }
62 | }
63 | });
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/statusbar/def/CollapsedStatusBarFragmentHook.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.statusbar.def;
2 |
3 | import static com.tianma.tweaks.miui.xp.wrapper.XposedWrapper.findAndHookMethod;
4 |
5 | import android.content.res.Resources;
6 | import android.os.Bundle;
7 | import android.view.Gravity;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.LinearLayout;
11 |
12 | import com.tianma.tweaks.miui.data.sp.XPrefContainer;
13 | import com.tianma.tweaks.miui.utils.XLogKt;
14 | import com.tianma.tweaks.miui.utils.rom.MiuiVersion;
15 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook;
16 | import com.tianma.tweaks.miui.xp.hook.systemui.helper.ResHelpers;
17 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper;
18 |
19 | import de.robv.android.xposed.XposedHelpers;
20 |
21 | public class CollapsedStatusBarFragmentHook extends BaseSubHook {
22 |
23 | private static final String CLASS_STATUS_BAR_FRAGMENT = "com.android.systemui.statusbar.phone.CollapsedStatusBarFragment";
24 |
25 | private boolean mSignalAlignLeft;
26 | private boolean mAlwaysShowStatusBarClock;
27 |
28 | public CollapsedStatusBarFragmentHook(ClassLoader classLoader, MiuiVersion miuiVersion) {
29 | super(classLoader, null, miuiVersion);
30 |
31 | // mSignalAlignLeft = XSPUtils.isSignalAlignLeft(xsp);
32 | mSignalAlignLeft = XPrefContainer.isSignalAlignLeft();
33 | // mAlwaysShowStatusBarClock = XSPUtils.alwaysShowStatusBarClock(xsp);
34 | mAlwaysShowStatusBarClock = XPrefContainer.getAlwaysShowStatusBarClock();
35 | }
36 |
37 | @Override
38 | public void startHook() {
39 | try {
40 | XLogKt.logD("Hooking CollapsedStatusBarFragment... ");
41 | if (mSignalAlignLeft) {
42 | hookOnViewCreated();
43 | }
44 |
45 | if (mAlwaysShowStatusBarClock) {
46 | hookClockVisibleAnimate();
47 | }
48 | } catch (Throwable t) {
49 | XLogKt.logE("Error occurs when hook CollapsedStatusBarFragment", t);
50 | }
51 | }
52 |
53 | // CollapsedStatusBarFragment#onViewCreated()
54 | private void hookOnViewCreated() {
55 | findAndHookMethod(CLASS_STATUS_BAR_FRAGMENT,
56 | getMClassLoader(),
57 | "onViewCreated",
58 | View.class,
59 | Bundle.class,
60 | new MethodHookWrapper() {
61 | @Override
62 | protected void after(MethodHookParam param) {
63 | ViewGroup phoneStatusBarView = (ViewGroup) XposedHelpers.getObjectField(param.thisObject, "mStatusBar");
64 | Resources res = phoneStatusBarView.getResources();
65 |
66 | View signalClusterViewContainer = phoneStatusBarView
67 | .findViewById(ResHelpers.getId(res, "signal_cluster_view"));
68 | ((ViewGroup) signalClusterViewContainer.getParent()).removeView(signalClusterViewContainer);
69 |
70 | if (getMMiuiVersion().getTime() >= MiuiVersion.V_19_5_7.getTime()) {
71 | try {
72 | LinearLayout contentsContainer = phoneStatusBarView
73 | .findViewById(ResHelpers.getId(res, "phone_status_bar_contents_container"));
74 | contentsContainer.setGravity(Gravity.CENTER_VERTICAL);
75 | contentsContainer.addView(signalClusterViewContainer, 0);
76 | } catch (Throwable t) {
77 | LinearLayout statusBarContents = phoneStatusBarView
78 | .findViewById(ResHelpers.getId(res, "status_bar_contents"));
79 | statusBarContents.setGravity(Gravity.CENTER_VERTICAL);
80 | statusBarContents.addView(signalClusterViewContainer, 0);
81 | }
82 | } else {
83 | LinearLayout statusBarContents = phoneStatusBarView
84 | .findViewById(ResHelpers.getId(res, "status_bar_contents"));
85 | statusBarContents.setGravity(Gravity.CENTER_VERTICAL);
86 | statusBarContents.addView(signalClusterViewContainer, 0);
87 | }
88 | }
89 | });
90 | }
91 |
92 | private void hookClockVisibleAnimate() {
93 | findAndHookMethod(CLASS_STATUS_BAR_FRAGMENT,
94 | getMClassLoader(),
95 | "clockVisibleAnimate",
96 | boolean.class,
97 | boolean.class,
98 | new MethodHookWrapper() {
99 | @Override
100 | protected void before(MethodHookParam param) {
101 | View mStatusClock = (View) XposedHelpers.getObjectField(param.thisObject, "mStatusClock");
102 | mStatusClock.setVisibility(View.VISIBLE);
103 | param.setResult(null);
104 | }
105 | });
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/statusbar/def/PhoneStatusBarViewHook.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.statusbar.def;
2 |
3 | import static com.tianma.tweaks.miui.xp.wrapper.XposedWrapper.findAndHookMethod;
4 |
5 | import android.content.Context;
6 | import android.content.res.Resources;
7 | import android.view.Gravity;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.LinearLayout;
11 | import android.widget.TextView;
12 |
13 | import com.tianma.tweaks.miui.cons.PrefConst;
14 | import com.tianma.tweaks.miui.data.sp.XPrefContainer;
15 | import com.tianma.tweaks.miui.utils.XLogKt;
16 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook;
17 | import com.tianma.tweaks.miui.xp.hook.systemui.SystemUIHook;
18 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo;
19 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper;
20 |
21 | import de.robv.android.xposed.XC_MethodHook;
22 |
23 | /**
24 | * 状态栏时钟居中显示
25 | * 适用版本 9.4.x+
26 | */
27 | public class PhoneStatusBarViewHook extends BaseSubHook {
28 |
29 | private static final String PACKAGE_NAME = SystemUIHook.PACKAGE_NAME;
30 |
31 | private static final String CLASS_PHONE_STATUS_BAR_VIEW = "com.android.systemui.statusbar.phone.PhoneStatusBarView";
32 | private static final String CLASS_STATUS_BAR = "com.android.systemui.statusbar.phone.StatusBar";
33 |
34 | private static final String CLASS_NOTIFICATION_ICON_CONTAINER = "com.android.systemui.statusbar.phone.NotificationIconContainer";
35 |
36 | private LinearLayout mCenterLayout;
37 |
38 | private boolean mAlignmentCenter = false;
39 | private boolean mAlignmentRight = false;
40 |
41 | public PhoneStatusBarViewHook(ClassLoader classLoader, AppInfo appInfo) {
42 | super(classLoader, appInfo);
43 | // String alignment = XSPUtils.getStatusBarClockAlignment(xsp);
44 | String alignment = XPrefContainer.getStatusBarClockAlignment();
45 | if (PrefConst.ALIGNMENT_CENTER.equals(alignment)) {
46 | mAlignmentCenter = true;
47 | mAlignmentRight = false;
48 | } else if (PrefConst.ALIGNMENT_RIGHT.equals(alignment)) {
49 | mAlignmentCenter = false;
50 | mAlignmentRight = true;
51 | }
52 | }
53 |
54 | public void startHook() {
55 | if (mAlignmentCenter || mAlignmentRight) {
56 | try {
57 | XLogKt.logD("Hooking PhoneStatusBarView...");
58 | hookSetBar();
59 | if (mAlignmentCenter) {
60 | hookGetActualWidth();
61 | }
62 | } catch (Throwable t) {
63 | XLogKt.logE("Error occurs when hook PhoneStatusBarView", t);
64 | }
65 | }
66 | }
67 |
68 | private void hookSetBar() {
69 | findAndHookMethod(CLASS_PHONE_STATUS_BAR_VIEW,
70 | getMClassLoader(),
71 | "setBar",
72 | CLASS_STATUS_BAR,
73 | new MethodHookWrapper() {
74 | @Override
75 | protected void after(MethodHookParam param) {
76 | prepareLayoutStatusBar(param);
77 | }
78 | });
79 | }
80 |
81 | private void prepareLayoutStatusBar(XC_MethodHook.MethodHookParam param) {
82 | // FrameLayout
83 | ViewGroup phoneStatusBarView = (ViewGroup) param.thisObject;
84 |
85 | Context context = phoneStatusBarView.getContext();
86 | Resources res = context.getResources();
87 |
88 | // LinearLayout statusBarContents = phoneStatusBarView.findViewById(
89 | // res.getIdentifier("status_bar_contents", "id", PACKAGE_NAME));
90 |
91 | TextView clock = phoneStatusBarView.findViewById(
92 | res.getIdentifier("clock", "id", PACKAGE_NAME));
93 | ((ViewGroup) clock.getParent()).removeView(clock);
94 |
95 | if (mAlignmentCenter) {
96 | // 注入新的居中的layout 到 phoneStatusBarView 中去
97 | mCenterLayout = new LinearLayout(context);
98 | LinearLayout.LayoutParams lp =
99 | new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
100 | mCenterLayout.setLayoutParams(lp);
101 | mCenterLayout.setGravity(Gravity.CENTER);
102 | phoneStatusBarView.addView(mCenterLayout);
103 |
104 | clock.setPaddingRelative(2, 0, 2, 0);
105 | clock.setGravity(Gravity.CENTER);
106 | mCenterLayout.addView(clock);
107 | } else if (mAlignmentRight) { // 居右对齐
108 | LinearLayout rightAreaLayout = phoneStatusBarView.findViewById(
109 | res.getIdentifier("system_icons", "id", PACKAGE_NAME));
110 | clock.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
111 | rightAreaLayout.addView(clock);
112 | }
113 | }
114 |
115 | private void hookGetActualWidth() {
116 | findAndHookMethod(CLASS_NOTIFICATION_ICON_CONTAINER,
117 | getMClassLoader(),
118 | "getActualWidth",
119 | new MethodHookWrapper() {
120 | @Override
121 | protected void after(MethodHookParam param) {
122 | if (mCenterLayout == null)
123 | return;
124 | if (mCenterLayout.getChildCount() == 0)
125 | return;
126 | View clock = mCenterLayout.getChildAt(0);
127 | int width = Math.round(mCenterLayout.getWidth() / 2f - clock.getWidth() / 2f) - 8;
128 | param.setResult(width);
129 | }
130 | });
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/statusbar/v20201109/CollapsedStatusBarFragmentHook20201109.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.statusbar.v20201109
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
7 | import com.tianma.tweaks.miui.utils.callMethod
8 | import com.tianma.tweaks.miui.utils.logD
9 | import com.tianma.tweaks.miui.utils.logE
10 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
11 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.ScreenBroadcastManager
12 | import com.tianma.tweaks.miui.xp.hook.systemui.screen.SimpleScreenListener
13 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
14 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
15 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
16 | import de.robv.android.xposed.XposedHelpers
17 |
18 | class CollapsedStatusBarFragmentHook20201109(classLoader: ClassLoader?, appInfo: AppInfo?) :
19 | BaseSubHook(classLoader, appInfo) {
20 |
21 | companion object {
22 | private const val CLASS_STATUS_BAR_FRAGMENT =
23 | "com.android.systemui.statusbar.phone.CollapsedStatusBarFragment"
24 | }
25 |
26 | private val mSignalAlignLeft: Boolean = XPrefContainer.isSignalAlignLeft
27 | private val mAlwaysShowStatusBarClock: Boolean = XPrefContainer.alwaysShowStatusBarClock
28 |
29 | // 是否可以展示状态栏时钟 (锁屏状态下不展示,梁平状态下有可能展示)
30 | private var canShowStatusBarClock = false
31 |
32 | override fun startHook() {
33 | try {
34 | logD("Hooking CollapsedStatusBarFragment... ")
35 | if (mSignalAlignLeft) {
36 | // nothing
37 | }
38 | if (mAlwaysShowStatusBarClock) {
39 | hookOnViewCreated()
40 | hookHideClock()
41 | }
42 | } catch (t: Throwable) {
43 | logE("Error occurs when hook CollapsedStatusBarFragment", t)
44 | }
45 | }
46 |
47 | // CollapsedStatusBarFragment#onViewCreated()
48 | private fun hookOnViewCreated() {
49 | XposedWrapper.findAndHookMethod(
50 | CLASS_STATUS_BAR_FRAGMENT,
51 | mClassLoader,
52 | "onViewCreated",
53 | View::class.java,
54 | Bundle::class.java,
55 | object : MethodHookWrapper() {
56 | override fun after(param: MethodHookParam) {
57 | val phoneStatusBarView =
58 | XposedHelpers.getObjectField(param.thisObject, "mStatusBar") as ViewGroup
59 |
60 | val context = phoneStatusBarView.context
61 | val statusBarFragment = param.thisObject
62 |
63 | val screenListener = object : SimpleScreenListener() {
64 | override fun onUserPresent() {
65 | // 屏幕解锁时,可以展示状态栏时钟
66 | canShowStatusBarClock = true
67 |
68 | // 展示时钟
69 | statusBarFragment.callMethod(
70 | "showClock",
71 | arrayOf(Boolean::class.java),
72 | arrayOf(true)
73 | )
74 | }
75 |
76 | override fun onScreenOff() {
77 | // 屏幕锁定时,不可以展示状态栏时钟
78 | canShowStatusBarClock = false
79 |
80 | statusBarFragment.callMethod(
81 | "hideClock",
82 | arrayOf(Boolean::class.java),
83 | arrayOf(true)
84 | )
85 | }
86 | }
87 | ScreenBroadcastManager.getInstance(context).registerListener(screenListener)
88 | }
89 | })
90 | }
91 |
92 | private fun hookHideClock() {
93 | XposedWrapper.findAndHookMethod(
94 | CLASS_STATUS_BAR_FRAGMENT,
95 | mClassLoader,
96 | "hideClock",
97 | Boolean::class.java,
98 | object : MethodHookWrapper() {
99 | override fun before(param: MethodHookParam?) {
100 | param ?: return
101 | // 拦截 hideClock()
102 | if (canShowStatusBarClock) {
103 | param.result = null
104 | }
105 | }
106 | }
107 | )
108 | }
109 |
110 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/statusbar/v20201109/StatusBarMobileViewHook20201109.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.statusbar.v20201109
2 |
3 | import android.widget.TextView
4 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
5 | import com.tianma.tweaks.miui.utils.logE
6 | import com.tianma.tweaks.miui.utils.logI
7 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
8 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
9 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
10 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
11 | import de.robv.android.xposed.XposedBridge
12 | import de.robv.android.xposed.XposedHelpers
13 |
14 | /**
15 | * StatusBarMobileView Hook, 用于 hook 自定义网络类型
16 | * 适用版本 MIUISystemUI(versionCode >= 202011090)
17 | */
18 | class StatusBarMobileViewHook20201109(
19 | classLoader: ClassLoader,
20 | appInfo: AppInfo
21 | ) : BaseSubHook(classLoader, appInfo) {
22 |
23 | companion object {
24 | private const val CLASS_STATUS_BAR_MOBILE_VIEW = "com.android.systemui.statusbar.StatusBarMobileView"
25 | }
26 |
27 | // private val isCustomNetworkTypeEnabled = XSPUtils.isCustomMobileNetworkEnabled(xsp)
28 | private val isCustomNetworkTypeEnabled = XPrefContainer.isCustomMobileNetworkEnabled
29 | private var customNetworkType: String = ""
30 |
31 | init {
32 | if (isCustomNetworkTypeEnabled) {
33 | // customNetworkType = XSPUtils.customMobileNetwork(xsp)
34 | customNetworkType = XPrefContainer.customMobileNetwork
35 | }
36 | }
37 |
38 | override fun startHook() {
39 | try {
40 | logI("Hooking StatusBarMobileView...")
41 |
42 | if (isCustomNetworkTypeEnabled) {
43 | hookUpdateState()
44 | }
45 |
46 | }catch (t: Throwable) {
47 | logE("Error occurs when hook StatusBarMobileView", t)
48 | }
49 | }
50 |
51 | private fun hookUpdateState() {
52 | val targetMethod = XposedWrapper.findMethodByNameIfExists(
53 | CLASS_STATUS_BAR_MOBILE_VIEW,
54 | mClassLoader,
55 | "updateState"
56 | )
57 |
58 | targetMethod?.also {
59 | XposedBridge.hookMethod(
60 | targetMethod,
61 | object: MethodHookWrapper() {
62 | override fun after(param: MethodHookParam?) {
63 | param?.also {
64 | val thisObj = param.thisObject
65 | val mMobileType = XposedHelpers.getObjectField(thisObj, "mMobileType") as TextView
66 | mMobileType.text = customNetworkType
67 | }
68 | }
69 | }
70 | )
71 | }
72 | }
73 |
74 |
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/statusbar/v20201109/StatusBarSignalPolicyHook20201109.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.statusbar.v20201109
2 |
3 | import com.tianma.tweaks.miui.data.sp.XPrefContainer
4 | import com.tianma.tweaks.miui.utils.logE
5 | import com.tianma.tweaks.miui.utils.logI
6 | import com.tianma.tweaks.miui.xp.hook.BaseSubHook
7 | import com.tianma.tweaks.miui.xp.utils.appinfo.AppInfo
8 | import com.tianma.tweaks.miui.xp.wrapper.MethodHookWrapper
9 | import com.tianma.tweaks.miui.xp.wrapper.XposedWrapper
10 | import de.robv.android.xposed.XposedHelpers
11 |
12 | /**
13 | * StatusBarSignalPolicy Hook, 用于处理 隐藏 VPN 图标
14 | * 适用版本 MIUISystemUI(versionCode >= 202011090)
15 | */
16 | class StatusBarSignalPolicyHook20201109(
17 | classLoader: ClassLoader,
18 | appInfo: AppInfo
19 | ) : BaseSubHook(classLoader, appInfo) {
20 |
21 | companion object {
22 | private const val CLASS_STATUS_BAR_POLICY = "com.android.systemui.statusbar.phone.StatusBarSignalPolicy"
23 | }
24 |
25 | // private val mHideVpnIcon: Boolean = XSPUtils.isHideVpnIcon(xsp)
26 | private val mHideVpnIcon: Boolean = XPrefContainer.isHideVpnIcon
27 |
28 | override fun startHook() {
29 |
30 | try {
31 | logI("Hooking StatusBarSignalPolicy...")
32 |
33 | if (mHideVpnIcon) {
34 | hookUpdateVpn()
35 | }
36 |
37 | } catch (t: Throwable) {
38 | logE("Error occurs when hook StatusBarSignalPolicy", t)
39 | }
40 |
41 | }
42 |
43 | private fun hookUpdateVpn() {
44 | XposedWrapper.findAndHookMethod(
45 | CLASS_STATUS_BAR_POLICY,
46 | mClassLoader,
47 | "updateVpn",
48 | object : MethodHookWrapper() {
49 | override fun after(param: MethodHookParam?) {
50 | param?.also {
51 | val thisObj = param.thisObject
52 | val mSlotVpn = XposedHelpers.getObjectField(thisObj, "mSlotVpn")
53 | val mIconController = XposedHelpers.getObjectField(thisObj, "mIconController")
54 | XposedHelpers.callMethod(mIconController, "setIconVisibility", mSlotVpn, false)
55 | }
56 | }
57 | }
58 | )
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/tick/TickObserver.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.tick;
2 |
3 | public interface TickObserver {
4 |
5 | void onTimeTick();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/tick/TimeTicker.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.tick;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 | import android.os.SystemClock;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class TimeTicker {
11 |
12 | private List mObserverList;
13 |
14 | private volatile static TimeTicker sTimeTicker;
15 |
16 | private Handler mSecondsHandler = new Handler(Looper.getMainLooper());
17 |
18 | private TimeTicker() {
19 | mObserverList = new ArrayList<>();
20 | }
21 |
22 | private Runnable mSecondsTicker = new Runnable() {
23 |
24 | @Override
25 | public void run() {
26 | long now = SystemClock.uptimeMillis();
27 | long next = now + (1000 - now % 1000);
28 | mSecondsHandler.postAtTime(this, next);
29 | for (TickObserver observer : mObserverList) {
30 | if (observer != null) {
31 | observer.onTimeTick();
32 | }
33 | }
34 | }
35 | };
36 |
37 | public static TimeTicker get() {
38 | if (sTimeTicker == null) {
39 | synchronized (TimeTicker.class) {
40 | if (sTimeTicker == null) {
41 | sTimeTicker = new TimeTicker();
42 | }
43 | }
44 | }
45 | return sTimeTicker;
46 | }
47 |
48 | public synchronized void registerObserver(TickObserver observer) {
49 | if (mObserverList.isEmpty()) {
50 | startTick();
51 | }
52 |
53 | if (!mObserverList.contains(observer)) {
54 | mObserverList.add(observer);
55 | }
56 | }
57 |
58 | public synchronized void unregisterObserver(TickObserver observer) {
59 | mObserverList.remove(observer);
60 |
61 | if (mObserverList.isEmpty()) {
62 | stopTick();
63 | }
64 | }
65 |
66 | private void startTick() {
67 | mSecondsHandler.post(mSecondsTicker);
68 | }
69 |
70 | private void stopTick() {
71 | mSecondsHandler.removeCallbacks(mSecondsTicker);
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/hook/systemui/weather/WeatherObserver.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.hook.systemui.weather;
2 |
3 | public interface WeatherObserver {
4 |
5 | void onWeatherChanged(String newWeatherInfo);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/utils/appinfo/AppInfo.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.utils.appinfo
2 |
3 | data class AppInfo(val packageName: String, val versionCode: Int, val versionName: String)
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/utils/appinfo/AppInfoHelper.kt:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.utils.appinfo
2 |
3 | import com.tianma.tweaks.miui.utils.logE
4 | import de.robv.android.xposed.XposedHelpers
5 | import de.robv.android.xposed.callbacks.XC_LoadPackage
6 | import java.io.File
7 |
8 | object AppInfoHelper {
9 |
10 | @JvmStatic
11 | fun getAppInfo(lpparam: XC_LoadPackage.LoadPackageParam?): AppInfo {
12 | var versionCode = 0
13 | var versionName = "0"
14 | var packageName = ""
15 |
16 | if (lpparam != null) {
17 | try {
18 | val packageParserCls = XposedHelpers.findClass("android.content.pm.PackageParser", lpparam.classLoader)
19 | val packageParser = packageParserCls.newInstance()
20 | val apkPath = File(lpparam.appInfo.sourceDir)
21 | val pkg = XposedHelpers.callMethod(packageParser, "parsePackage", apkPath, 0)
22 | versionCode = XposedHelpers.getIntField(pkg, "mVersionCode")
23 | versionName = XposedHelpers.getObjectField(pkg, "mVersionName") as String
24 | packageName = lpparam.packageName
25 | } catch (throwable: Throwable) {
26 | logE("Parse package info failed", throwable)
27 | }
28 | }
29 |
30 | return AppInfo(packageName, versionCode, versionName)
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/utils/appinfo/AppVersionConst.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.utils.appinfo;
2 |
3 | public interface AppVersionConst {
4 |
5 | int SYSTEM_UI_V201912130 = 201912130;
6 |
7 | int SYSTEM_UI_V202011090 = 202011090;
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/wrapper/MethodHookWrapper.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.wrapper;
2 |
3 | import com.tianma.tweaks.miui.utils.XLogKt;
4 | import com.tianma.tweaks.miui.utils.XLogKt;
5 |
6 | import de.robv.android.xposed.XC_MethodHook;
7 |
8 | public abstract class MethodHookWrapper extends XC_MethodHook {
9 |
10 | @Override
11 | final protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
12 | try {
13 | before(param);
14 | } catch (Throwable t) {
15 | XLogKt.logE("Error in hook %s", param.method.getName(), t);
16 | }
17 | }
18 |
19 | protected void before(MethodHookParam param) {
20 | }
21 |
22 | @Override
23 | final protected void afterHookedMethod(MethodHookParam param) throws Throwable {
24 | try {
25 | after(param);
26 | } catch (Throwable t) {
27 | XLogKt.logE("Error in hook %s", param.method.getName(), t);
28 | }
29 | }
30 |
31 | protected void after(MethodHookParam param) {
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tianma/tweaks/miui/xp/wrapper/XposedWrapper.java:
--------------------------------------------------------------------------------
1 | package com.tianma.tweaks.miui.xp.wrapper;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.tianma.tweaks.miui.utils.XLogKt;
6 |
7 | import java.lang.reflect.Method;
8 | import java.lang.reflect.Modifier;
9 | import java.util.HashMap;
10 | import java.util.Set;
11 |
12 | import de.robv.android.xposed.XC_MethodHook;
13 | import de.robv.android.xposed.XposedBridge;
14 | import de.robv.android.xposed.XposedHelpers;
15 |
16 | /**
17 | * Xposed Wrapper Utils
18 | */
19 | public class XposedWrapper {
20 |
21 | private static final HashMap methodCache = new HashMap();
22 |
23 | private XposedWrapper() {
24 | }
25 |
26 | public static Class> findClass(String className, ClassLoader classLoader) {
27 | try {
28 | return XposedHelpers.findClass(className, classLoader);
29 | } catch (Throwable t) {
30 | XLogKt.logE("Class not found: %s", className);
31 | return null;
32 | }
33 | }
34 |
35 | public static XC_MethodHook.Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) {
36 | try {
37 | return XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback);
38 | } catch (Throwable t) {
39 | XLogKt.logE("Error in hook %s#%s", className, methodName, t);
40 | return null;
41 | }
42 | }
43 |
44 | public static XC_MethodHook.Unhook findAndHookMethod(Class> clazz, String methodName, Object... parameterTypesAndCallback) {
45 | try {
46 | return XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback);
47 | } catch (Throwable t) {
48 | XLogKt.logE("Error in hook %s#%s", clazz.getName(), methodName, t);
49 | return null;
50 | }
51 | }
52 |
53 | public static Set hookAllConstructors(Class> hookClass, XC_MethodHook callback) {
54 | try {
55 | return XposedBridge.hookAllConstructors(hookClass, callback);
56 | } catch (Throwable t) {
57 | XLogKt.logE("Error in hookAllConstructors: %s", hookClass.getName(), t);
58 | return null;
59 | }
60 | }
61 |
62 | public static XC_MethodHook.Unhook findAndHookConstructor(Class> clazz, Object... parameterTypesAndCallback) {
63 | try {
64 | return XposedHelpers.findAndHookConstructor(clazz, parameterTypesAndCallback);
65 | } catch (Throwable t) {
66 | XLogKt.logE("Error in findAndHookConstructor: %s", clazz.getName(), t);
67 | return null;
68 | }
69 | }
70 |
71 | public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) {
72 | try {
73 | return XposedHelpers.findAndHookConstructor(className, classLoader, parameterTypesAndCallback);
74 | } catch (Throwable t) {
75 | XLogKt.logE("Error in findAndHookConstructor: %s", className, t);
76 | return null;
77 | }
78 | }
79 |
80 | // 通过方法名查找方法,模糊查询
81 | @Nullable
82 | public static Method findMethodByNameIfExists(String className, ClassLoader classLoader, String methodName) {
83 | return findMethodByNameIfExists(findClass(className, classLoader), methodName);
84 | }
85 |
86 | // 通过方法名查找方法,模糊查询
87 | @Nullable
88 | public static Method findMethodByNameIfExists(Class> clazz, String methodName) {
89 | if (clazz == null) {
90 | return null;
91 | }
92 | String fullMethodName = clazz.getName() + '#' + methodName + "#fuzzyMatch";
93 |
94 | if (methodCache.containsKey(fullMethodName)) {
95 | Method method = methodCache.get(fullMethodName);
96 | if (method == null)
97 | throw new NoSuchMethodError(fullMethodName);
98 | return method;
99 | }
100 |
101 | Method fuzzyMatch = null;
102 | Class> clz = clazz;
103 | boolean considerPrivateMethods = true;
104 | do {
105 | for (Method method : clz.getDeclaredMethods()) {
106 | // don't consider private methods of superclasses
107 | if (!considerPrivateMethods && Modifier.isPrivate(method.getModifiers()))
108 | continue;
109 |
110 | // compare name and parameters
111 | if (method.getName().equals(methodName)) {
112 | // get accessible version of method
113 | fuzzyMatch = method;
114 | break;
115 | }
116 | }
117 |
118 | if (fuzzyMatch != null) {
119 | break;
120 | }
121 |
122 | considerPrivateMethods = false;
123 | } while ((clz = clz.getSuperclass()) != null);
124 |
125 | if (fuzzyMatch != null) {
126 | fuzzyMatch.setAccessible(true);
127 | }
128 | methodCache.put(fullMethodName, fuzzyMatch);
129 | return fuzzyMatch;
130 | }
131 |
132 | private static String getParametersString(Class>... clazzes) {
133 | StringBuilder sb = new StringBuilder("(");
134 | boolean first = true;
135 | for (Class> clazz : clazzes) {
136 | if (first)
137 | first = false;
138 | else
139 | sb.append(",");
140 |
141 | if (clazz != null)
142 | sb.append(clazz.getCanonicalName());
143 | else
144 | sb.append("null");
145 | }
146 | sb.append(")");
147 | return sb.toString();
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/app/src/main/res/color/tag_view_text_color_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tag_view_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | -
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | -
21 |
22 |
23 |
24 |
25 |
26 |
27 | -
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
13 |
14 |
20 |
21 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_hitokoto_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
29 |
30 |
42 |
43 |
52 |
53 |
64 |
65 |
77 |
78 |
87 |
88 |
99 |
100 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/preference_category.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/tag_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-sw360dp-v13/values-preference.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | 0dp
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 | #90008577
8 |
9 | #FFFFFF
10 | #CDCDCD
11 | #000000
12 | #BDBDBD
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 20dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #7C24AB
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/dropdown_statusbar_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
15 |
16 |
21 |
22 |
27 |
28 |
32 |
33 |
38 |
39 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/keyguard_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
16 |
17 |
18 |
19 |
24 |
25 |
29 |
30 |
38 |
39 |
44 |
45 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/main_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
33 |
34 |
38 |
39 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/statusbar_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
17 |
18 |
22 |
23 |
28 |
29 |
33 |
34 |
40 |
41 |
46 |
47 |
51 |
52 |
56 |
57 |
61 |
62 |
66 |
67 |
71 |
72 |
77 |
78 |
83 |
84 |
--------------------------------------------------------------------------------
/art/cn/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/art/cn/01.png
--------------------------------------------------------------------------------
/art/cn/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/art/cn/02.png
--------------------------------------------------------------------------------
/art/en/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/art/en/01.png
--------------------------------------------------------------------------------
/art/en/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/art/en/02.png
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.4.31'
5 | ext.and_res_guard_version = '1.2.20'
6 |
7 | repositories {
8 | google()
9 | jcenter()
10 |
11 | }
12 | dependencies {
13 | classpath 'com.android.tools.build:gradle:4.1.0'
14 |
15 | // NOTE: Do not place your application dependencies here; they belong
16 | // in the individual module build.gradle files
17 | // classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.0'
18 |
19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
20 | classpath "com.tencent.mm:AndResGuard-gradle-plugin:$and_res_guard_version" // AndResGuard
21 | }
22 | }
23 |
24 | allprojects {
25 | repositories {
26 | google()
27 | jcenter()
28 |
29 | }
30 | }
31 |
32 | task clean(type: Delete) {
33 | delete rootProject.buildDir
34 | }
35 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # Custom Properties start
15 | tianma.keystore.path=/Users/tianma/Codes/Keystore/tianma.jks
16 | tianma.signature.path=/Users/tianma/Codes/Keystore/signature.properties
17 | # Custom Properties end
18 |
19 | # 表示使用 androidx
20 | android.useAndroidX=true
21 | # 表示将第三方库迁移到 androidx
22 | android.enableJetifier=true
23 | # 禁用R8 Android code shrinker
24 | # android.enableR8=true
25 |
26 | android.injected.testOnly=false
27 |
28 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/XMiTools/16e2f92e1e2863de79b8dd5360a05595828dd1f6/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Mar 04 21:42:26 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------