5 |
6 | Everyone is permitted to copy and distribute verbatim or modified
7 | copies of this license document, and changing it is allowed as long
8 | as the name is changed.
9 |
10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12 |
13 | 0. You just DO WHAT THE FUCK YOU WANT TO.
14 |
15 |
--------------------------------------------------------------------------------
/app/Makefile:
--------------------------------------------------------------------------------
1 | lib/AndroidHiddenAPI.jar: \
2 | AndroidHiddenAPI/android/app/*.java \
3 | AndroidHiddenAPI/android/app/usage/*.java \
4 | AndroidHiddenAPI/android/content/*.java \
5 | AndroidHiddenAPI/android/content/pm/*.java \
6 | AndroidHiddenAPI/android/os/*.java \
7 | AndroidHiddenAPI/android/view/*.java \
8 | AndroidHiddenAPI/com/android/server/am/*.java \
9 | AndroidHiddenAPI/com/android/internal/app/*.java
10 |
11 | javac -cp ${ANDROID_HOME}/platforms/android-27/android.jar \
12 | AndroidHiddenAPI/android/app/*.java \
13 | AndroidHiddenAPI/android/app/usage/*.java \
14 | AndroidHiddenAPI/android/content/*.java \
15 | AndroidHiddenAPI/android/content/pm/*.java \
16 | AndroidHiddenAPI/android/os/*.java \
17 | AndroidHiddenAPI/android/view/*.java \
18 | AndroidHiddenAPI/com/android/server/am/*.java \
19 | AndroidHiddenAPI/com/android/internal/app/*.java
20 |
21 | @rm AndroidHiddenAPI/android/app/ActivityManager\$$*.class
22 |
23 | @cd AndroidHiddenAPI; jar -cvf ../lib/AndroidHiddenAPI.jar \
24 | android/app/*.class \
25 | android/app/usage/*.class \
26 | android/content/*.class \
27 | android/content/pm/*.class \
28 | android/os/*.class \
29 | android/view/*.class \
30 | com/android/server/am/*.class \
31 | com/android/internal/app/*.class
32 |
33 | @find AndroidHiddenAPI/ -name "*.class" -exec rm {} \;
34 |
35 | clean:
36 | @rm -rf lib/AndroidHiddenAPI.jar
37 | @find AndroidHiddenAPI/ -name "*.class" -exec rm -fv {} \;
38 |
--------------------------------------------------------------------------------
/app/README.md:
--------------------------------------------------------------------------------
1 | # English
2 |
3 | ## Why Brevent
4 |
5 | "`Brevent`" hajacks several system API to prevent not-in-use apps in `prevent list` from running or keep running. Furthermore, it applies to system apps too, specially, support google-family apps(`GAPPS`).
6 |
7 | Not-in-use packages in `prevent list` can only run:
8 |
9 | - some other app call it's activity (share, launcher)
10 | - widget on home, however, it can only run 30 seconds
11 | - sync service if you allow, it can only run 30 seconds too
12 | - system services (excluding normal gapps), or alipay's service
13 |
14 | **NOTE**: When Google Play Services(`GMS`) and related apps are in `prevent list`, only `GAPPS` and `GCM`-apps can use it. However, you cannot receive `GCM` message if `GMS` is not running.
15 |
16 | **WARNING**: Don't prevent `system` apps nor daily apps, otherwise, you may miss important message.
17 |
18 | **WARNING**: Don't prevent "`Xposed Installer`", otherwise the module won't active when you update it.
19 |
20 | "`Brevent`" should work from Android 4.4 to 6.0. However, I mainly use 7.1.
21 |
22 | ## How to use
23 |
24 | 1. Patch your ROM with "`Brevent`".
25 | 2. Open "`Brevent`", then add/remove application to/from prevent list.
26 | 3. Use android normally, press `back` or remove it from recent task to exit, and press `HOME` for pause.
27 |
28 | And "`Brevent`" would keep non-"service" processes, of cource it cannot turn to "service".
29 |
30 | ## Special Search
31 |
32 | - `-3` for `third` party apps
33 | - `-a` for `a`ll apps (default show third party apps and gapps)
34 | - `-s` for `s`ystem apps
35 | - `-e` for `e`nabled apps
36 | - `-r` for `r`unning apps
37 | - `-g` for `g`apps, i.e. apps from google
38 | - `-sg` for `s`ystem apps excluding `g`apps
39 |
40 | ## [Importance for Processes](http://developer.android.com/intl/zh-tw/reference/android/app/ActivityManager.RunningAppProcessInfo.html#constants):
41 |
42 | ### background
43 |
44 | This process contains background code that is expendable.
45 |
46 | ### empty
47 |
48 | This process is empty of any actively running code.
49 |
50 | ### foreground
51 |
52 | This process is running the foreground UI; that is, it is the thing currently at the top of the screen that the user is interacting with.
53 |
54 | ### foreground service (since Android 6.0)
55 |
56 | This process is running a foreground service, for example to perform music playback even while the user is not immediately in the app. This generally indicates that the process is doing something the user actively cares about.
57 |
58 | ### gone (since Android 5.0)
59 |
60 | This process does not exist.
61 |
62 | ### perceptible
63 |
64 | This process is not something the user is directly aware of, but is otherwise perceptable to them to some degree.
65 |
66 | ### service
67 |
68 | This process is contains services that should remain running. These are background services apps have started, not something the user is aware of, so they may be killed by the system relatively freely (though it is generally desired that they stay running as long as they want to).
69 |
70 | ### top sleeping (since Android 6.0)
71 |
72 | This process is running the foreground UI, but the device is asleep so it is not visible to the user. This means the user is not really aware of the process, because they can not see or interact with it, but it is quite important because it what they expect to return to once unlocking the device.
73 |
74 | ### visible
75 |
76 | This process is running something that is actively visible to the user, though not in the immediate foreground. This may be running a window that is behind the current foreground (so paused and with its state saved, not interacting with the user, but visible to them to some degree); it may also be running other services under the system's control that it inconsiders important.
77 |
78 | ## Project
79 |
80 | Project: [Brevent - GitHub](https://github.com/liudongmiao/Brevent). If you like, feel free to donate.
81 |
82 | # 中文
83 |
84 | ## 黑域介绍
85 |
86 | “`黑域`”通过劫持几个系统API,保证`阻止列表`里的应用只在需要时才启动,同时支持谷歌家族应用。
87 |
88 | 没有运行的`阻止列表`应用只会在以下几种情况下启动:
89 |
90 | - 启动器或者第三方应用启动活动(Activity),如手动打开、分享、部分支付等;
91 | - 桌面小部件定时更新,但是只能运行30秒;
92 | - 同步开启时的定时同步,也只能运行30秒;
93 | - 除`谷歌服务`外的系统服务,或者支付宝的支付服务;
94 | - 其它可能的用户行为引起的启动。
95 |
96 | **注意**:当`谷歌服务`在阻止列表时,只有`谷歌家族应用`和第三方的`GCM`应用可以使用。同时,当有任何一个`谷歌家族应用`没有退出时,都不会退出`谷歌服务`。当然,只有`GMS`运行时才能接收`GCM`消息,并唤醒相应应用。
97 |
98 | **警告**:请谨慎阻止“系统应用”,以及常用应用。要不然,你可能无法及时收到短信或其它重要消息。“`黑域`”不会显示和系统同一签名的系统应用,也不会显示系统内置的启动器。
99 |
100 | **警告**:请不要阻止“`Xposed Installer`”,否则模块更新时,无法更新模块路径,导致重启以后无法加载模块。
101 |
102 | **提示**:有些用户无法或不愿分清`HOME`与`返回键`区别,可以开启“强行停止后台程序”,在离开程序一段时间后并黑屏时退出应用。这项功能默认关闭。
103 |
104 | “`黑域`”支持Android 4.4到7.1,本人主要在7.1上测试。
105 |
106 | ## 使用说明
107 |
108 | 1. 给手机安装`黑域`补丁。
109 | 2. 打开“`黑域`”,配置`阻止列表`(这个只需要一次)。
110 | 3. 正常使用手机,临时退出时按`HOME`,不用时按`返回键`退出或者从最近列表划掉。
111 |
112 | 同时,“`黑域`”不杀非`服务`的程序,但是保证非`服务`类进程不会变成`服务`在后台***一直***运行。
113 |
114 | ## 特别搜索
115 |
116 | - `-3` 用户安装的第`三`方程序
117 | - `-a` 全部程序(默认仅展示第三方和谷歌家族的程序)
118 | - `-s` 系统预装的程序
119 | - `-e` 没有阻止的程序
120 | - `-r` 正在运行的程序
121 | - `-g` `谷`歌家族的程序
122 | - `-sg` 除谷歌家族外的系统程序
123 |
124 | ## [进程级别](http://developer.android.com/intl/zh-tw/reference/android/app/ActivityManager.RunningAppProcessInfo.html#constants)
125 |
126 | ### 后台(background)
127 |
128 | 可被回收的后台进程。(译者注:或译缓存的后台进程,不需要主动清理。)
129 |
130 | ### 空(empty)
131 |
132 | 进程不包含任何正在运行的代码。
133 |
134 | ### 前台(foreground)
135 |
136 | 进程正在前台运行,也是你正在使用的应用。(译者注:当你在“`黑域`”中查看进程状态时,“`黑域`”永远是前台。)
137 |
138 | ### 前台服务(foreground service, 自Android 6.0)
139 |
140 | 进程包含前台服务,比如播放音乐等,通常表明正在处理一些用户关心的事情。
141 |
142 | ### 无(gone, 自Android 5.0)
143 |
144 | 进程不存在。
145 |
146 | ### 察觉(perceptible)
147 |
148 | 虽然用户不能直接注意到,但是某种层次上可以感觉到(译者注:如输入法)。
149 |
150 | ### 服务(service)
151 |
152 | 包含需要持续运行的服务。这些是已经启动的后台服务,用户并不能注意到,它们也有可能被系统回收(虽然从设计上会被一直运行下去)。
153 |
154 | ### 前台休眠(top sleeping,自Android 6.0)
155 |
156 | 进程在前台运行,但是设备正在休眠,所以用户看不见。这意味着用户并不能真正注意到它,因为看不见也无法操作,但是当设备解锁时期望立即看到,所以也非常重要。
157 |
158 | ### 可见(visible)
159 |
160 | 进程对用户可见,虽然不一定是最近的前台。它可能运行在当前前台后的窗口,虽然已经暂停并且保存状态,也无法使用,但是某种层次上用户能见到;也可能是系统控制下的其它重要服务。
161 |
162 | ## 项目
163 |
164 | “`黑域`”代码暂时不开源,项目地址:[Brevent - GitHub](https://github.com/liudongmiao/Brevent)。如果喜欢,请随意捐赠。
165 |
--------------------------------------------------------------------------------
/app/assets/about.en.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Brevent
7 |
12 |
13 |
14 | Why Brevent
15 |
16 | "Brevent
" hajacks several system API to prevent not-in-use apps in prevent list
from running or keep running. Furthermore, it applies to system apps too, specially, support google-family apps(GAPPS
).
17 |
18 | Not-in-use packages in prevent list
can only run:
19 |
20 |
21 | - some other app call it's activity (share, launcher)
22 | - widget on home, however, it can only run 30 seconds
23 | - sync service if you allow, it can only run 30 seconds too
24 | - system services (excluding normal gapps), or alipay's service
25 |
26 |
27 | NOTE: When Google Play Services(GMS
) and related apps are in prevent list
, only GAPPS
and GCM
-apps can use it. However, you cannot receive GCM
message if GMS
is not running.
28 |
29 | WARNING: Don't prevent system
apps nor daily apps, otherwise, you may miss important message.
30 |
31 | WARNING: Don't prevent "Xposed Installer
", otherwise the module won't active when you update it.
32 |
33 | "Brevent
" should work from android 4.4 to 7.1. However, I mainly use 7.1.
34 |
35 | How to use
36 |
37 |
38 | - Patch your ROM with "
Brevent
".
39 | - Open "
Brevent
", then add/remove application to/from prevent list.
40 | - Use android normally, press
back
or remove it from recent task to exit, and press HOME
for pause.
41 |
42 |
43 | "Brevent
" would keep non-"service" processes, of cource it cannot turn to "service".
44 |
45 | Special Search
46 |
47 |
48 | -3
for third
party apps
49 | -a
for a
ll apps (default show third party apps and gapps)
50 | -s
for s
ystem apps
51 | -e
for e
nabled apps
52 | -r
for r
unning apps
53 | -g
for g
apps, i.e. apps from google
54 | -sg
for s
ystem apps excluding g
apps
55 |
56 |
57 |
58 |
59 | background
60 |
61 | This process contains background code that is expendable.
62 |
63 | empty
64 |
65 | This process is empty of any actively running code.
66 |
67 | foreground
68 |
69 | This process is running the foreground UI; that is, it is the thing currently at the top of the screen that the user is interacting with.
70 |
71 | foreground service (since Android 6.0)
72 |
73 | This process is running a foreground service, for example to perform music playback even while the user is not immediately in the app. This generally indicates that the process is doing something the user actively cares about.
74 |
75 | gone (since Android 5.0)
76 |
77 | This process does not exist.
78 |
79 | perceptible
80 |
81 | This process is not something the user is directly aware of, but is otherwise perceptable to them to some degree.
82 |
83 | service
84 |
85 | This process is contains services that should remain running. These are background services apps have started, not something the user is aware of, so they may be killed by the system relatively freely (though it is generally desired that they stay running as long as they want to).
86 |
87 | top sleeping (since Android 6.0)
88 |
89 | This process is running the foreground UI, but the device is asleep so it is not visible to the user. This means the user is not really aware of the process, because they can not see or interact with it, but it is quite important because it what they expect to return to once unlocking the device.
90 |
91 | visible
92 |
93 | This process is running something that is actively visible to the user, though not in the immediate foreground. This may be running a window that is behind the current foreground (so paused and with its state saved, not interacting with the user, but visible to them to some degree); it may also be running other services under the system's control that it inconsiders important.
94 |
95 | Project
96 |
97 | Project: Brevent - GitHub. If you like, feel free to donate.
98 |
99 |
100 |
--------------------------------------------------------------------------------
/app/assets/about.zh.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 黑域
7 |
12 |
13 |
14 | 黑域介绍
15 |
16 | “黑域
”通过劫持几个系统API,保证阻止列表
里的应用只在需要时才启动,同时支持谷歌家族应用。
17 |
18 | 没有运行的阻止列表
应用只会在以下几种情况下启动:
19 |
20 |
21 | - 启动器或者第三方应用启动活动(Activity),如手动打开、分享、部分支付等;
22 | - 桌面小部件定时更新,但是只能运行30秒;
23 | - 同步开启时的定时同步,也只能运行30秒;
24 | - 除
谷歌服务
外的系统服务,或者支付宝的支付服务;
25 | - 其它可能的用户行为引起的启动。
26 |
27 |
28 | 注意:当谷歌服务
在阻止列表时,只有谷歌家族应用
和第三方的GCM
应用可以使用。同时,当有任何一个谷歌家族应用
没有退出时,都不会退出谷歌服务
。当然,只有GMS
运行时才能接收GCM
消息,并唤醒相应应用。
29 |
30 | 警告:请谨慎阻止“系统应用”,以及常用应用。要不然,你可能无法及时收到短信或其它重要消息。“黑域
”不会显示和系统同一签名的系统应用,也不会显示系统内置的启动器。
31 |
32 | 警告:请不要阻止“Xposed Installer
”,否则模块更新时,无法更新模块路径,导致重启以后无法加载模块。
33 |
34 | 提示:有些用户无法或不愿分清HOME
与返回键
区别,可以开启“强行停止后台程序”,在离开程序一段时间后并黑屏时退出应用。这项功能默认关闭。
35 |
36 | “黑域
”支持Android 4.4到7.1,本人主要在7.1上测试。
37 |
38 | 使用说明
39 |
40 |
41 | - 给系统打“
黑域
”补丁。
42 | - 重启后,打开“
黑域
”,配置阻止列表
(这个只需要一次)。
43 | - 正常使用手机,临时退出时按
HOME
,不用时按返回键
退出或者从最近列表划掉。
44 |
45 |
46 | “黑域
”不杀非服务
的程序,但是保证非服务
类进程不会变成服务
在后台一直运行。
47 |
48 | 特别搜索
49 |
50 |
51 | -3
用户安装的第三
方程序
52 | -a
全部程序(默认仅展示第三方和谷歌家族的程序)
53 | -s
系统预装的程序
54 | -e
没有阻止的程序
55 | -r
正在运行的程序
56 | -g
谷
歌家族的程序
57 | -sg
除谷歌家族外的系统程序
58 |
59 |
60 |
61 |
62 | 后台(background)
63 |
64 | 可被回收的后台进程。(译者注:或译缓存的后台进程,不需要主动清理。)
65 |
66 | 空(empty)
67 |
68 | 进程不包含任何正在运行的代码。
69 |
70 | 前台(foreground)
71 |
72 | 进程正在前台运行,也是你正在使用的应用。(译者注:当你在“黑域
”中查看进程状态时,“黑域
”永远是前台。)
73 |
74 | 前台服务(foreground service, 自Android 6.0)
75 |
76 | 进程包含前台服务,比如播放音乐等,通常表明正在处理一些用户关心的事情。
77 |
78 | 无(gone, 自Android 5.0)
79 |
80 | 进程不存在。
81 |
82 | 察觉(perceptible)
83 |
84 | 虽然用户不能直接注意到,但是某种层次上可以感觉到(译者注:如输入法)。
85 |
86 | 服务(service)
87 |
88 | 包含需要持续运行的服务。这些是已经启动的后台服务,用户并不能注意到,它们也有可能被系统回收(虽然从设计上会被一直运行下去)。
89 |
90 | 前台休眠(top sleeping,自Android 6.0)
91 |
92 | 进程在前台运行,但是设备正在休眠,所以用户看不见。这意味着用户并不能真正注意到它,因为看不见也无法操作,但是当设备解锁时期望立即看到,所以也非常重要。
93 |
94 | 可见(visible)
95 |
96 | 进程对用户可见,虽然不一定是最近的前台。它可能运行在当前前台后的窗口,虽然已经暂停并且保存状态,也无法使用,但是某种层次上用户能见到;也可能是系统控制下的其它重要服务。
97 |
98 | 项目
99 |
100 | “黑域
”代码暂时不开源,项目地址:Brevent - GitHub。如果喜欢,请随意捐赠。
101 |
102 |
103 |
--------------------------------------------------------------------------------
/app/assets/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/assets/wechat.png
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 |
2 | plugins {
3 | id 'com.android.application'
4 | }
5 |
6 |
7 | def isIde = { ->
8 | def ide = false
9 | def taskRequests = project.gradle.startParameter.taskRequests
10 | if (taskRequests?.empty) {
11 | ide = true
12 | } else {
13 | taskRequests[0].args.any {
14 | if (it.endsWith('generateDebugSources') || it.endsWith('assembleDebug')) {
15 | ide = true
16 | return true
17 | }
18 | }
19 | return ide
20 | }
21 | }
22 |
23 | android {
24 | compileSdkVersion 30
25 | buildToolsVersion "27.0.0"
26 |
27 |
28 | defaultConfig {
29 | versionCode 14
30 | applicationId "me.piebridge.prevent"
31 | versionName "1.4"
32 | minSdkVersion 29
33 | targetSdkVersion 30
34 | maxSdkVersion 30
35 | buildConfigField "boolean", "RELEASE", "true"
36 | buildConfigField "String", "DONATE_ALIPAY", "null"
37 | buildConfigField "String", "DONATE_PAYPAL", "null"
38 | buildConfigField "String", "EMAIL", "null"
39 |
40 | }
41 |
42 | buildTypes {
43 | release {
44 | lintOptions {
45 | checkReleaseBuilds false
46 | abortOnError false
47 | }
48 | minifyEnabled false
49 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
50 | signingConfig signingConfigs.debug
51 | }
52 | debug {
53 | signingConfig signingConfigs.debug
54 | }
55 | }
56 |
57 | sourceSets {
58 | main {
59 | manifest.srcFile 'AndroidManifest.xml'
60 |
61 | if (isIde()) {
62 | java.srcDirs = ['src', 'services/core/java', /*'AndroidHiddenAPI'*/]
63 | } else {
64 | java.srcDirs = ['src', 'services/core/java']
65 | }
66 |
67 | resources.srcDirs = ['src']
68 | aidl.srcDirs = ['src']
69 | renderscript.srcDirs = ['src']
70 | res.srcDirs = ['res']
71 | assets.srcDirs = ['assets']
72 | }
73 | androidTest.setRoot('tests')
74 | }
75 |
76 | if (new File("ant.properties").exists()) {
77 | Properties properties = new Properties()
78 | properties.load(new FileInputStream(file("ant.properties")))
79 |
80 | signingConfigs {
81 | release {
82 | storeFile file(properties['key.store'])
83 | storePassword properties['key.store.password']
84 | keyAlias properties['key.alias']
85 | keyPassword properties['key.alias.password']
86 | }
87 | }
88 |
89 | buildTypes {
90 | release {
91 | signingConfig signingConfigs.release
92 | }
93 | debug {
94 | signingConfig signingConfigs.release
95 | }
96 | }
97 | }
98 |
99 | compileOptions {
100 | sourceCompatibility JavaVersion.VERSION_1_8
101 | targetCompatibility JavaVersion.VERSION_1_8
102 | }
103 | }
104 |
105 | dependencies {
106 | implementation 'androidx.viewpager:viewpager:1.0.0'
107 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
108 | implementation 'androidx.preference:preference:1.1.0'
109 | compileOnly fileTree(dir: 'lib', include: ['*.jar'])
110 | implementation 'androidx.appcompat:appcompat:1.2.0'
111 | implementation 'com.google.android.material:material:1.2.0'
112 | implementation project(":groupedadapter")
113 | }
114 |
115 | tasks.withType(JavaCompile) {
116 | options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation'
117 | if (!isIde()) {
118 | options.compilerArgs << '-Xbootclasspath/p:' + fileTree(dir: 'lib', include: ['*.jar']).asPath
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/lib/hidenapi.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/lib/hidenapi.jar
--------------------------------------------------------------------------------
/app/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/res/drawable-hdpi/ic_menu_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-hdpi/ic_menu_block.png
--------------------------------------------------------------------------------
/app/res/drawable-hdpi/ic_menu_prevent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-hdpi/ic_menu_prevent.png
--------------------------------------------------------------------------------
/app/res/drawable-hdpi/ic_menu_recover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-hdpi/ic_menu_recover.png
--------------------------------------------------------------------------------
/app/res/drawable-hdpi/ic_menu_star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-hdpi/ic_menu_star.png
--------------------------------------------------------------------------------
/app/res/drawable-hdpi/ic_menu_stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-hdpi/ic_menu_stop.png
--------------------------------------------------------------------------------
/app/res/drawable-hdpi/paypal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-hdpi/paypal.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/ic_menu_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/ic_menu_block.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/ic_menu_prevent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/ic_menu_prevent.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/ic_menu_recover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/ic_menu_recover.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/ic_menu_star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/ic_menu_star.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/ic_menu_stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/ic_menu_stop.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/icon_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/icon_right.png
--------------------------------------------------------------------------------
/app/res/drawable-xhdpi/paypal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable-xhdpi/paypal.png
--------------------------------------------------------------------------------
/app/res/drawable/about.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/res/drawable/contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/res/drawable/holo_red_dark_ripple_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/res/drawable/settings.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/res/drawable/system_app.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/res/drawable/user_app.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/res/drawable/violet2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/app/res/drawable/violet2.jpg
--------------------------------------------------------------------------------
/app/res/layout/about.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/res/layout/activity_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
20 |
21 |
26 |
--------------------------------------------------------------------------------
/app/res/layout/adapter_expandable_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/res/layout/donate.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
26 |
27 |
38 |
39 |
50 |
51 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/app/res/layout/item.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
25 |
26 |
35 |
36 |
43 |
44 |
52 |
53 |
54 |
62 |
63 |
--------------------------------------------------------------------------------
/app/res/layout/list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
18 |
19 |
20 |
21 |
25 |
26 |
30 |
31 |
36 |
37 |
44 |
45 |
46 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/res/layout/nav_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
--------------------------------------------------------------------------------
/app/res/layout/progress_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/app/res/layout/progress_dialog_horizonal.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
11 |
12 |
--------------------------------------------------------------------------------
/app/res/menu/bottom_nv.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/res/menu/drawer_nv.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Brevent
5 |
6 | Dies ist keine Releaseversion und könnte Dein Gerät unnutzbar machen. Falls Du nichts testen willst, deinstalliere diese Version bitte und wechsle zu einer Releaseversion.
7 |
8 | Brevent sollte im internen Speicher installiert werden.
9 | Please patch your ROM with "Brevent" (English).
10 |
11 |
12 | Bitte konfiguriere die Blockier-Liste.
13 | Blockier-Liste auf %1$d aktualisiert
14 | ROM wird nicht unterstützt: Bitte melde den Fehler an den Entwickler.
15 |
16 |
17 | Bitte starte Dein Gerät neu, um die aktuelle Version zu aktivieren
18 | Geräte-Neustart
19 | Bist Du sicher?
20 | Anwendungen
21 | Blockier-Liste
22 | Automatisch blockieren
23 | Blockieren entfernen
24 |
25 |
26 | \"-3\" für Benutzeranwendungen
27 | \"-s\" für Systemanwendungen
28 |
29 |
30 | Schema wechseln
31 | Benutzerhandbuch
32 |
33 |
34 | Status wird überprüft, bitte warten. . .
35 | Lädt. . .
36 | (nicht gestartet)
37 |
38 | Hintergrund
39 | Vordergrund
40 | Leer
41 | Wahrnehmbar
42 | Service
43 | Sichtbar
44 | Gestoppt
45 | Vordergrund-Service
46 | Vordergrund-Schlafend
47 | Service (Nicht gestartet)
48 | Inaktiv
49 |
50 |
51 | App-Info
52 | Öffnen
53 | Deinstallieren
54 | App-Benachrichtigungen
55 |
56 |
57 | Prozesse nicht speichern
58 | Beenden der Anwendung erzwingen, sobald der Benutzer alle Activities beendet hat (nicht empfohlen)
59 | Sicherung
60 | Blockier-Liste auf externen Speicher sichern
61 | Synchronisieren sperren
62 | Verhindert, dass Apps deaktivierte Synchronisationseinträge wieder einschalten
63 | Automatisch verhindern
64 | Neue Apps automatisch blockieren
65 | App-Ruhezustand
66 | Versetze Apps in Standby, anstatt ein Beenden zu erzwingen
67 | Unbekannte Aufrufe erlauben
68 | Anderen Apps erlauben, Services zu starten. Dies ist nützlich, um manche Apps direkt durch Benachrichtigungen oder Widgets zu öffnen, es könnte jedoch auch unerwünschte Auswirkungen haben
69 | Wichtige Systemanwendungen blockieren
70 | Diese sind nicht anklickbar
71 | Beenden von Hintergrund-Apps erzwingen
72 | %1$s nach Bildschirm aus und Inaktivität der Hintergrund-Apps
73 | Nie (Empfohlen)
74 | 5 Minuten
75 | 10 Minuten
76 | 15 Minuten
77 | 30 Minuten
78 | 1 Stunde
79 |
80 |
81 | Version
82 | Feedback
83 | Fehler melden
84 | Erweiterte Einstellungen
85 | Bitte wähle einen E-Mail-Client aus
86 |
87 |
88 | Spenden sind gern gesehen, wenn Du magst.
89 | QR-Code aus Album wählen
90 | PayPal
91 |
92 |
93 |
--------------------------------------------------------------------------------
/app/res/values-in/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Brevent
5 |
6 | Ini bukan versi rilis, dan mungkin memblokir perangkatmu. Jika kamu tidak suka untuk berpartisipasi, tolong hapus pemasangan dan kembali gunakan versi rilis.
7 |
8 | \"Brevent\" harus dipasang di penyimpanan internal.
9 | Please patch your ROM with "Brevent" (English).
10 |
11 |
12 | Tolong atur daftar pencegahan.
13 | Daftar pencegahan diupdate ke %1$d
14 | ROM tidak didukung, tolong laporkan kesalahan.
15 |
16 |
17 | Tolong boot ulang untuk mengaktifkan versi sekarang
18 | Boot ulang
19 | Apa kamu yakin?
20 | Aplikasi
21 | Daftar Pencegahan
22 | Pencegahan Otomatis
23 | Hapus Pencegahan
24 |
25 |
26 | \"-3\" untuk aplikasi pengguna
27 | \"-s\" untuk aplikasi sistem
28 |
29 |
30 | Ubah tema
31 | Bantuan pengguna
32 |
33 |
34 | Mengambil status, silahkan tunggu. . .
35 | Memuat. . .
36 | (tidak berjalan)
37 |
38 | Latar belakang
39 | Latar depan
40 | Kosong
41 | Terlihat
42 | Layanan
43 | Nyata
44 | Hilang
45 | Latar depan Layanan
46 | Tidur tertinggi
47 | Layanan (Belum dimulai)
48 | Tidak aktif
49 |
50 |
51 | Info Aplikasi
52 | Buka
53 | Hapus Pemasangan
54 | Pemberitahuan aplikasi
55 |
56 |
57 | Jangan simpan proses
58 | Paksa berhenti aplikasi setelah pengguna meniadakan semua aktifitas (disarankan off)
59 | Cadangkan
60 | Cadangkan daftar pencegahan ke penyimpanan eksternal
61 | Kunci Sync
62 | Cegah aplikasi dari menyalakan item sync yang dinonaktifkan
63 | Mencegah otomatis
64 | Otomatis mencegah aplikasi baru
65 | Aplikasi siaga
66 | Gunakan siaga daripada henti paksa aplikasi
67 | Ijinkan pengirim tak diketahui
68 | Ijinkan pengirim tak diketahui untuk memulai layanan, yang berguna untuk membuka beberapa aplikasi secara langsung melalui pemberitahuan atau widget, bagaimanapun, itu mungkin mempunyai efek samping
69 | Paksa berhenti aplikasi sistem penting
70 | Itu tidak terkontrol
71 | Paksa berhenti aplikasi latar belakang
72 | Layar mati, dan setelah %1$s ketidakaktifan dengan aplikasi latar belakang
73 | Jangan pernah (Disarankan)
74 | 5 menit
75 | 10 menit
76 | 15 menit
77 | 30 menit
78 | 1 jam
79 |
80 |
81 | Versi
82 | Umpan balik
83 | Laporkan kesalahan
84 | Setelan lanjutan
85 | Silahkan pilih EMail klien
86 |
87 |
88 | Jangan ragu untuk donasi jika kamu suka.
89 | Pilih Kode QR dari Album
90 | PayPal
91 |
92 |
93 |
--------------------------------------------------------------------------------
/app/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 黑域 Pro
5 |
6 | 这是测试版本,可能导致您的机器无法运行。如果不想参加测试,请恢复至正式版本。
7 |
8 | “黑域”应该安装在内部存储。
9 | 请给ROM打“黑域”补丁。
10 |
11 |
12 | 请配置“阻止列表”。
13 | 成功更新阻止列表,共%1$d条
14 | 暂不支持您的系统,请报告问题。
15 |
16 |
17 | 请重启激活当前版本
18 | 重启
19 | 你确定?
20 | 所有程序
21 | 阻止列表
22 | 自动阻止
23 | 不再阻止
24 | 运行中
25 | 未运行
26 |
27 | \"-3\"为已下载应用
28 | \"-s\"为系统应用
29 |
30 |
31 | 切换主题
32 | 用户指南
33 | 关于
34 | 设置
35 | 联系
36 | 打开抽屉
37 | 关闭抽屉
38 | 用户应用
39 | 系统应用
40 |
41 |
42 | 正在获取数据,请稍候. . .
43 | 正在加载. . .
44 | (没有运行)
45 | 后台
46 | 前台
47 | 空
48 | 察觉
49 | 服务
50 | 可见
51 | 无
52 | 前台服务
53 | 前台休眠
54 | 服务(没有开始)
55 | 未启用
56 | 音频焦点
57 | VPN连接
58 | 桌面
59 | 输入法
60 |
61 | 程序信息
62 | 打开
63 | 卸载
64 | 应用通知
65 |
66 |
67 | 不保留进程
68 | 用户退出后强行停止程序(强烈推荐关闭)
69 | 备份
70 | 将阻止列表备份到外部存储
71 | 同步锁定
72 | 阻止应用开启已经关闭的同步设置
73 | 自动阻止
74 | 自动阻止新装程序
75 | 休眠应用
76 | 使用休眠代替强行停止
77 | 允许未知唤醒
78 | 这个有助于在通知栏或者小部件中直接打开应用,但也有些副作用
79 | 强行停止重要系统应用
80 | 这些应用不能选中
81 | 强行停止后台程序
82 | 后台程序无操作%1$s后(屏幕关闭时处理)
83 | 永不(推荐)
84 | 5分钟
85 | 10分钟
86 | 15分钟
87 | 30分钟
88 | 1小时
89 | 优化Vpn连接应用
90 | 优化Vpn
91 | 优化音频焦点应用
92 | 优化音频
93 |
94 |
95 | 版本
96 | 反馈
97 | 报告问题
98 | 强迫症设置
99 | 请选择邮件客户端
100 |
101 |
102 | 如果喜欢,请随(duo)意(duo)捐赠
103 | 请点击菜单,从相册选取二维码
104 | 贝宝
105 |
106 |
107 |
--------------------------------------------------------------------------------
/app/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 黑域
5 |
6 | 這是測試版本,可能導致您的機器無法運行。如果不想參加測試,請恢復至正式版本。
7 |
8 | 『黑域』應當安裝在內部存儲。
9 | 請給ROM打『黑域』補丁。
10 |
11 |
12 | 請配置『阻止列表』。
13 | 成功更新阻止列表,共%1$d條
14 | 暫不支持您的系統,請回報錯誤。
15 |
16 |
17 | 請重啟激活當前版本
18 | 重啓
19 | 您確定?
20 | 所有程式
21 | 阻止列表
22 | 自動阻止
23 | 不再阻止
24 |
25 |
26 | \"-3\"為已下載程式
27 | \"-s\"為系統程式
28 |
29 |
30 | 切換主題
31 | 用戶指南
32 |
33 |
34 | 正在獲取數據,請稍候. . .
35 | 正在加載. . .
36 | (沒有運行)
37 | 背景
38 | 前景
39 | 空
40 | 察覺
41 | 服務
42 | 可見
43 | 無
44 | 前景服務
45 | 前景休眠
46 | 服務(沒有開始)
47 | 未啟用
48 |
49 |
50 | 程式資訊
51 | 開啟
52 | 解除安裝
53 | 程式通知
54 |
55 |
56 | 不要保留處理程式
57 | 使用者退出後強制停止程式(極度推薦關閉)
58 | 備份
59 | 將阻止列表備份到外部記憶體
60 | 同步鎖定
61 | 阻止程式開啟已經關閉的同步設置
62 | 自動阻止
63 | 自動阻止新裝程式
64 | 休眠程式
65 | 使用休眠代替強制停止
66 | 允許未知喚醒
67 | 這個有助於在通知欄或者小部件中直接打開應用,但也有些副作用
68 | 強制停止重要係統應用
69 | 這些應用不能選中
70 | 強制停止背景程式
71 | 背景程式閒置%1$s後(螢幕關閉時處理)
72 | 永不(推薦)
73 | 5分鐘
74 | 10分鐘
75 | 15分鐘
76 | 30分鐘
77 | 1小時
78 |
79 |
80 | 回饋
81 | 版本
82 | 回報錯誤
83 | 進階設定
84 | 請選擇郵件客戶端
85 |
86 |
87 | 如果喜歡,請隨意捐贈
88 | PayPal
89 | 從相簿中選擇QR Code
90 |
91 |
92 |
--------------------------------------------------------------------------------
/app/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #FFCBCBCB
7 |
--------------------------------------------------------------------------------
/app/res/values/donottranslate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | - -1
6 | - 300
7 | - 600
8 | - 900
9 | - 1800
10 | - 3600
11 |
12 |
13 |
14 | - @string/force_stop_never
15 | - @string/force_stop_5min
16 | - @string/force_stop_10min
17 | - @string/force_stop_15min
18 | - @string/force_stop_30min
19 | - @string/force_stop_1hour
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
13 |
18 |
19 |
24 |
25 |
30 |
31 |
38 |
39 |
44 |
45 |
50 |
51 |
56 |
57 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/services/core/java/com/android/server/am/ActivityStackSupervisor.java:
--------------------------------------------------------------------------------
1 | package com.android.server.am;
2 |
3 | import android.os.IBinder;
4 |
5 | public abstract class ActivityStackSupervisor implements IBinder {
6 | /* access modifiers changed from: package-private */
7 | public void cleanUpRemovedTaskLocked(TaskRecord taskRecord, boolean z, boolean z2) {
8 | try {
9 | cleanUpRemovedTaskLocked$Pr(taskRecord, z, z2);
10 | } finally {
11 | if (z) {
12 | PreventRunningUtils.onCleanUpRemovedTask(taskRecord.getBaseIntent());
13 | }
14 | }
15 | }
16 |
17 | private void cleanUpRemovedTaskLocked$Pr(TaskRecord taskRecord, boolean z, boolean z2) {
18 | throw new UnsupportedOperationException();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/services/core/java/com/android/server/am/PreventRunningHook.java:
--------------------------------------------------------------------------------
1 | package com.android.server.am;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.pm.ApplicationInfo;
7 | import android.net.Uri;
8 | import java.util.Set;
9 |
10 | public interface PreventRunningHook {
11 | boolean hookBindService(Intent intent);
12 |
13 | boolean hookStartProcessLocked(Context context, ApplicationInfo applicationInfo, String str, ComponentName componentName);
14 |
15 | boolean hookStartService(Intent intent);
16 |
17 | boolean isExcludingStopped(String str);
18 |
19 | int match(int i, Object obj, String str, String str2, String str3, Uri uri, Set set);
20 |
21 | void onAppDied(Object obj);
22 |
23 | void onBroadcastIntent(Intent intent);
24 |
25 | void onCleanUpRemovedTask(String str);
26 |
27 | void onDestroyActivity(Object obj);
28 |
29 | void onLaunchActivity(Object obj);
30 |
31 | void onMoveActivityTaskToBack(String str);
32 |
33 | void onResumeActivity(Object obj);
34 |
35 | void onStartHomeActivity(String str);
36 |
37 | void onUserLeavingActivity(Object obj);
38 |
39 | void setMethod(String str);
40 |
41 | void setSender(String str);
42 |
43 | void setVersion(int i);
44 |
45 | void onActivityRequestAudioFocus(int uid,int pid,String clientId,String packageName );
46 |
47 | void onActivityAbandonAudioFocus(int uid,int pid,String clientId);
48 |
49 | void onActivityLostAudioFocusOnDeath(String clientId);
50 |
51 | void onActivityEstablishVpnConnection(String packageName);
52 |
53 | void onVpnConnectionDisconnected();
54 | }
55 |
--------------------------------------------------------------------------------
/app/services/core/java/com/android/server/am/PreventRunningUtils.java:
--------------------------------------------------------------------------------
1 | package com.android.server.am;
2 |
3 | import android.app.IApplicationThread;
4 | import android.content.ComponentName;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.content.pm.ApplicationInfo;
8 | import android.net.Uri;
9 | import android.os.Binder;
10 | import android.os.Build;
11 | import android.os.IBinder;
12 | import android.os.ServiceManager;
13 |
14 | import com.android.server.wm.ActivityRecord;
15 |
16 | import java.util.Set;
17 |
18 | public class PreventRunningUtils {
19 | private static ActivityManagerService ams;
20 | private static PreventRunning mPreventRunning = new PreventRunning();
21 |
22 | private PreventRunningUtils() {
23 | }
24 |
25 | private static ActivityManagerService getAms() {
26 | if (ams == null) {
27 | ams = (ActivityManagerService) ServiceManager.getService("activity");
28 | }
29 | return ams;
30 | }
31 |
32 | public static boolean isExcludingStopped(Intent intent) {
33 | String action = intent.getAction();
34 | return (intent.getFlags() & 48) == 16 && action != null && mPreventRunning.isExcludingStopped(action);
35 | }
36 |
37 | public static int match(IntentFilter intentFilter, String str, String str2, String str3, Uri uri, Set set, String str4) {
38 | int match = intentFilter.match(str, str2, str3, uri, set, str4);
39 | if (match >= 0) {
40 | return mPreventRunning.match(match, intentFilter, str, str2, str3, uri, set);
41 | }
42 | return match;
43 | }
44 |
45 | public static boolean hookStartProcessLocked(String str, ApplicationInfo applicationInfo, boolean z, int i, String str2, ComponentName componentName) {
46 | return mPreventRunning.hookStartProcessLocked(getAms().mContext, applicationInfo, str2, componentName);
47 | }
48 |
49 | public static int onStartActivity(int i, IApplicationThread iApplicationThread, String str, Intent intent) {
50 | ProcessRecord recordForAppLocked;
51 | if (i >= 0 && intent != null && ((intent.hasCategory("android.intent.category.HOME") || intent.hasCategory("android.intent.category.LAUNCHER")) && (recordForAppLocked = getAms().getRecordForAppLocked(iApplicationThread)) != null)) {
52 | mPreventRunning.onStartHomeActivity(recordForAppLocked.info.packageName);
53 | }
54 | return i;
55 | }
56 |
57 | public static void onAppDied(ProcessRecord processRecord) {
58 | mPreventRunning.onAppDied(processRecord);
59 | }
60 |
61 | public static boolean returnFalse() {
62 | return false;
63 | }
64 |
65 | public static boolean returnFalse(boolean z) {
66 | return z && !mPreventRunning.isActiviated();
67 | }
68 |
69 | public static void onCleanUpRemovedTask(Intent intent) {
70 | if (intent != null && intent.getComponent() != null) {
71 | mPreventRunning.onCleanUpRemovedTask(intent.getComponent().getPackageName());
72 | }
73 | }
74 |
75 | public static void onMoveActivityTaskToBack(IBinder iBinder) {
76 | ActivityRecord forToken = forToken(iBinder);
77 | mPreventRunning.onMoveActivityTaskToBack(forToken != null ? forToken.packageName : null);
78 | }
79 |
80 | public static void setSender(IApplicationThread iApplicationThread) {
81 | ProcessRecord recordForAppLocked = getAms().getRecordForAppLocked(iApplicationThread);
82 | mPreventRunning.setSender(recordForAppLocked != null ? recordForAppLocked.info.packageName : String.valueOf(Binder.getCallingUid()));
83 | }
84 |
85 | public static void clearSender() {
86 | mPreventRunning.setSender((String) null);
87 | }
88 |
89 | public static boolean hookStartService(IApplicationThread iApplicationThread, Intent intent) {
90 | return mPreventRunning.hookStartService(intent);
91 | }
92 |
93 | public static boolean hookBindService(IApplicationThread iApplicationThread, IBinder iBinder, Intent intent) {
94 | return mPreventRunning.hookBindService(intent);
95 | }
96 |
97 | public static void onBroadcastIntent(Intent intent) {
98 | mPreventRunning.onBroadcastIntent(intent);
99 | }
100 |
101 | public static void onUserLeavingActivity(IBinder iBinder, boolean z, boolean z2) {
102 | if (z2) {
103 | mPreventRunning.onUserLeavingActivity(forToken(iBinder));
104 | }
105 | }
106 |
107 | public static void onResumeActivity(IBinder iBinder) {
108 | mPreventRunning.onResumeActivity(forToken(iBinder));
109 | }
110 |
111 | public static void onDestroyActivity(IBinder iBinder) {
112 | mPreventRunning.onDestroyActivity(forToken(iBinder));
113 | }
114 |
115 | public static void onActivityRequestAudioFocus(int uid,int pid,String clientId,String packageName ){
116 | mPreventRunning.onActivityRequestAudioFocus(uid,pid,clientId,packageName);
117 | }
118 | public static void onActivityAbandonAudioFocus(int uid,int pid,String clientId) {
119 | mPreventRunning.onActivityAbandonAudioFocus(uid,pid,clientId);
120 | }
121 |
122 | public static void onActivityLostAudioFocusOnDeath(String clientId) {
123 | mPreventRunning.onActivityLostAudioFocusOnDeath(clientId);
124 | }
125 |
126 | public static void onLaunchActivity(IBinder iBinder) {
127 | mPreventRunning.onLaunchActivity(forToken(iBinder));
128 | }
129 |
130 | public static void onActivityEstablishVpnConnection(String packageName) {
131 | mPreventRunning.onActivityEstablishVpnConnection(packageName);
132 | }
133 |
134 | public static void onVpnConnectionDisconnected() {
135 | mPreventRunning.onVpnConnectionDisconnected();
136 | }
137 |
138 |
139 | private static ActivityRecord forToken(IBinder iBinder) {
140 |
141 | return ActivityRecord.forTokenLocked(iBinder);
142 | //return ActivityRecord.forToken(iBinder);
143 | }
144 |
145 |
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/common/CommonLog.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.common;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by thom on 2016/11/7.
7 | */
8 |
9 | public class CommonLog {
10 |
11 | public static final String TAG = "Prevent";
12 |
13 | private CommonLog() {
14 |
15 | }
16 |
17 | public static void v(String msg, Throwable t) {
18 | Log.v(TAG, msg, t);
19 | }
20 |
21 | public static void d(String msg) {
22 | Log.d(TAG, msg);
23 | }
24 |
25 | public static void i(String msg) {
26 | Log.i(TAG, msg);
27 | }
28 |
29 | public static void e(String msg, Throwable t) {
30 | Log.e(TAG, msg, t);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/common/Configuration.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.common;
2 |
3 | import android.os.Bundle;
4 |
5 | /**
6 | * Created by thom on 16/2/21.
7 | */
8 | public class Configuration {
9 |
10 | private final Bundle bundle;
11 |
12 | private static Configuration mConfiguration = new Configuration();
13 |
14 | private Configuration() {
15 | this.bundle = new Bundle();
16 | }
17 |
18 | public static Configuration getDefault() {
19 | return mConfiguration;
20 | }
21 |
22 | public long getForceStopTimeout() {
23 | return bundle.getLong(PreventIntent.KEY_FORCE_STOP_TIMEOUT, -1);
24 | }
25 |
26 | public boolean isAllowEmptySender() {
27 | return bundle.getBoolean(PreventIntent.KEY_ALLOW_EMPTY_SENDER, true);
28 | }
29 |
30 | public boolean isAutoPrevent() {
31 | return bundle.getBoolean(PreventIntent.KEY_AUTO_PREVENT, true);
32 | }
33 |
34 | public boolean isDestroyProcesses() {
35 | return bundle.getBoolean(PreventIntent.KEY_DESTROY_PROCESSES, false);
36 | }
37 |
38 | public boolean isLockSyncSettings() {
39 | return bundle.getBoolean(PreventIntent.KEY_LOCK_SYNC_SETTINGS, false);
40 | }
41 |
42 | public boolean isStopSignatureApps() {
43 | return bundle.getBoolean(PreventIntent.KEY_STOP_SIGNATURE_APPS, true);
44 | }
45 |
46 | public boolean isUseAppStandby() {
47 | return bundle.getBoolean(PreventIntent.KEY_USE_APP_STANDBY, false);
48 | }
49 |
50 | public boolean optimizeAudio() {
51 | return bundle.getBoolean(PreventIntent.KEY_OPTIMIZE_AUDIO, false);
52 | }
53 |
54 | public boolean optimizeVpn() {
55 | return bundle.getBoolean(PreventIntent.KEY_OPTIMIZE_VPN, false);
56 | }
57 |
58 | public Bundle getBundle() {
59 | return new Bundle(bundle);
60 | }
61 |
62 | public void updateBundle(Bundle bundle) {
63 | for (String key : PreventIntent.KEYS_LONG) {
64 | if (bundle.containsKey(key)) {
65 | long value = bundle.getLong(key);
66 | CommonLog.d("update " + key + " to " + value);
67 | this.bundle.putLong(key, value);
68 | }
69 | }
70 | for (String key : PreventIntent.KEYS_BOOLEAN) {
71 | if (bundle.containsKey(key)) {
72 | boolean value = bundle.getBoolean(key);
73 | CommonLog.d("update " + key + " to " + value);
74 | this.bundle.putBoolean(key, value);
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/common/ExternalFileUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.common;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.File;
6 |
7 | /**
8 | * Created by thom on 16/2/11.
9 | */
10 | public class ExternalFileUtils {
11 |
12 | private ExternalFileUtils() {
13 |
14 | }
15 |
16 | public static File[] getExternalFilesDirs(Context context) {
17 | File[] files;
18 | files = context.getExternalFilesDirs(null);
19 | if (files == null) {
20 | files = new File[0];
21 | }
22 | return files;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/common/FileUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.common;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.os.Environment;
6 |
7 | import java.io.BufferedReader;
8 | import java.io.BufferedWriter;
9 | import java.io.File;
10 | import java.io.FileReader;
11 | import java.io.FileWriter;
12 | import java.io.IOException;
13 | import java.util.Set;
14 | import java.util.TreeSet;
15 |
16 | /**
17 | * Created by thom on 16/2/11.
18 | */
19 | public class FileUtils {
20 |
21 | public static final String PREVENT_LIST = "prevent.list";
22 |
23 | private static final int MAX_WAIT = 3000;
24 | private static final int SINGLE_WAIT = 100;
25 |
26 | private FileUtils() {
27 |
28 | }
29 |
30 | private static void makeSure(File lock) {
31 | File parent = lock.getParentFile();
32 | if (parent.isFile()) {
33 | parent.delete();
34 | }
35 | if (!parent.isDirectory()) {
36 | parent.mkdirs();
37 | }
38 | while (lock.exists() && System.currentTimeMillis() - lock.lastModified() < MAX_WAIT) {
39 | try {
40 | Thread.sleep(SINGLE_WAIT);
41 | } catch (InterruptedException e) { // NOSONAR
42 | // do nothing
43 | }
44 | }
45 | }
46 |
47 | public static void save(String path, Set packages) {
48 | File lock = new File(path + ".lock");
49 | makeSure(lock);
50 | try {
51 | BufferedWriter writer = new BufferedWriter(new FileWriter(lock));
52 | for (String key : packages) {
53 | writer.write(key);
54 | writer.write("\n");
55 | }
56 | writer.close();
57 | lock.renameTo(new File(path));
58 | } catch (IOException e) {
59 | CommonLog.e("cannot save " + path, e);
60 | }
61 | }
62 |
63 | public static void save(String path, Bundle bundle) {
64 | File lock = new File(path + ".lock");
65 | makeSure(lock);
66 | try {
67 | BufferedWriter writer = new BufferedWriter(new FileWriter(lock));
68 | for (String key : new TreeSet(bundle.keySet())) {
69 | writer.write(key);
70 | writer.write("=");
71 | writer.write(String.valueOf(bundle.get(key)));
72 | writer.write("\n");
73 | }
74 | writer.close();
75 | lock.renameTo(new File(path));
76 | } catch (IOException e) {
77 | CommonLog.e("cannot save " + path, e);
78 | }
79 | }
80 |
81 | public static Set load(File file) {
82 | Set packages = new TreeSet();
83 | if (!file.exists()) {
84 | return packages;
85 | }
86 | try {
87 | String line;
88 | BufferedReader reader = new BufferedReader(new FileReader(file));
89 | while ((line = reader.readLine()) != null) {
90 | if (line.startsWith("#")) {
91 | continue;
92 | }
93 | int index = line.indexOf('=');
94 | if (index != -1) {
95 | line = line.substring(0, index);
96 | }
97 | line = line.trim();
98 | packages.add(line);
99 | }
100 | reader.close();
101 | CommonLog.i("load " + file.getAbsolutePath() + ", size: " + packages.size());
102 | } catch (IOException e) {
103 | CommonLog.e("cannot load " + file.getAbsolutePath(), e);
104 | }
105 | return packages;
106 | }
107 |
108 |
109 | public static Set load(Context context, String prevent) {
110 | Set packages = load(new File(prevent));
111 | return packages;
112 | }
113 |
114 | public static boolean eraseFiles(File path) {
115 | if (path == null) {
116 | return false;
117 | }
118 | if (path.isDirectory()) {
119 | String[] files = path.list();
120 | if (files != null) {
121 | for (String file : files) {
122 | eraseFiles(new File(path, file));
123 | }
124 | }
125 | }
126 | return path.delete();
127 | }
128 |
129 | }
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/common/PackageUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.common;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageManager;
7 | import android.content.pm.ResolveInfo;
8 | import android.net.Uri;
9 | import android.provider.Settings;
10 | import android.view.inputmethod.InputMethodInfo;
11 | import android.view.inputmethod.InputMethodManager;
12 |
13 | import java.util.Arrays;
14 | import java.util.Collection;
15 | import java.util.HashSet;
16 | import java.util.List;
17 | import java.util.Set;
18 |
19 | import me.piebridge.prevent.BuildConfig;
20 |
21 | /**
22 | * Created by thom on 15/7/23.
23 | */
24 | public class PackageUtils {
25 |
26 | public static final int FIRST_APPLICATION_UID = 10000;
27 |
28 | private static Set launchers;
29 |
30 | private static Set inputMethodPackages = new HashSet();
31 |
32 | private static String smsDefaultApplication;
33 |
34 | private static final Collection IMPORT_PACKAGES = Arrays.asList(
35 | "de.robv.android.xposed.installer",
36 | "eu.chainfire.supersu",
37 | "eu.chainfire.supersu.pro",
38 | BuildConfig.APPLICATION_ID
39 | );
40 |
41 | private PackageUtils() {
42 |
43 | }
44 |
45 | public static boolean isSystemPackage(int flags) {
46 | return (flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
47 | }
48 |
49 | public static boolean isSystemSignaturePackage(PackageManager pm, String packageName) {
50 | return pm.checkSignatures("android", packageName) != PackageManager.SIGNATURE_NO_MATCH;
51 | }
52 |
53 | private static synchronized void initLauncher(PackageManager pm) {
54 | if (launchers == null) {
55 | launchers = new HashSet();
56 | Intent intent = new Intent(Intent.ACTION_MAIN);
57 | intent.addCategory(Intent.CATEGORY_HOME);
58 | for (ResolveInfo resolveInfo : pm.queryIntentActivities(intent, 0)) {
59 | launchers.add(resolveInfo.activityInfo.packageName);
60 | }
61 | }
62 | }
63 |
64 | public static boolean isLauncher(PackageManager pm, String packageName) {
65 | if (launchers == null) {
66 | initLauncher(pm);
67 | }
68 | return launchers.contains(packageName);
69 | }
70 |
71 | public static boolean canPrevent(PackageManager pm, String packageName) {
72 | try {
73 | ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
74 | return appInfo.uid >= FIRST_APPLICATION_UID && (!isSystemPackage(appInfo.flags) || canPreventSystemPackage(pm, appInfo));
75 | } catch (PackageManager.NameNotFoundException e) {
76 | e.printStackTrace();
77 | return false;
78 | }
79 | }
80 |
81 | public static boolean canPrevent(PackageManager pm, ApplicationInfo appInfo) {
82 | return appInfo.uid >= FIRST_APPLICATION_UID && (!isSystemPackage(appInfo.flags) || canPreventSystemPackage(pm, appInfo));
83 | }
84 |
85 | private static boolean canPreventSystemPackage(PackageManager pm, ApplicationInfo appInfo) {
86 | // cannot prevent launcher
87 | if (isLauncher(pm, appInfo.packageName)) {
88 | return false;
89 | }
90 | // can prevent system packages with launcher
91 | if (pm.getLaunchIntentForPackage(appInfo.packageName) != null) {
92 | return true;
93 | }
94 | if (isSystemSignaturePackage(pm, BuildConfig.APPLICATION_ID)) {
95 | // shouldn't happen, but for some abnormal rom
96 | return false;
97 | } else {
98 | return !isSystemSignaturePackage(pm, appInfo.packageName);
99 | }
100 | }
101 |
102 | public static String getPackageName(Intent intent) {
103 | Uri data = intent.getData();
104 | if (data != null) {
105 | return data.getSchemeSpecificPart();
106 | } else {
107 | return null;
108 | }
109 | }
110 |
111 | public static boolean equals(Object a, Object b) { // NOSONAR
112 | return (a == b) || (a != null && a.equals(b));
113 | }
114 |
115 |
116 | public static boolean isInputMethod(Context context,String name) {
117 | if (inputMethodPackages.isEmpty()) {
118 | initInputMethods(context);
119 | }
120 | return inputMethodPackages.contains(name);
121 | }
122 |
123 | private static void initInputMethods(Context context) {
124 | InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
125 | List inputMethods = inputMethodManager.getEnabledInputMethodList();
126 | final int count = inputMethods == null ? 0 : inputMethods.size();
127 | for (int i = 0; i < count; ++i) {
128 | inputMethodPackages.add(inputMethods.get(i).getPackageName());
129 | }
130 | }
131 |
132 | public static void clearInputMethodPackages() {
133 | inputMethodPackages.clear();
134 | }
135 |
136 |
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/common/PreventIntent.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.common;
2 |
3 | import android.net.Uri;
4 |
5 | import me.piebridge.prevent.Manifest;
6 |
7 | /**
8 | * Created by thom on 15/7/12.
9 | */
10 | public final class PreventIntent {
11 |
12 | public static final String NAMESPACE = "me.piebridge.prevent.";
13 |
14 | // for ui - manager
15 | public static final String ACTION_GET_PACKAGES = NAMESPACE + "GET_PACKAGES";
16 | public static final String ACTION_GET_PROCESSES = NAMESPACE + "GET_PROCESSES";
17 | public static final String ACTION_GET_INFO = NAMESPACE + "GET_INFO";
18 | public static final String ACTION_UPDATE_PREVENT = NAMESPACE + "UPDATE_PREVENT";
19 | public static final String ACTION_SYSTEM_LOG = NAMESPACE + "SYSTEM_LOG";
20 | public static final String ACTION_UPDATE_CONFIGURATION = NAMESPACE + "UPDATE_CONFIGURATION";
21 | public static final String ACTION_SOFT_REBOOT = NAMESPACE + "SOFT_REBOOT";
22 | public static final String ACTION_REBOOT = NAMESPACE + "REBOOT";
23 | public static final String ACTION_NOT_SUPPORTED = NAMESPACE + "NOT_SUPPORTED";
24 |
25 | public static final String EXTRA_PACKAGES = NAMESPACE + "PACKAGES";
26 | public static final String EXTRA_PREVENT = NAMESPACE + "PREVENT";
27 | public static final String EXTRA_CONFIGURATION = NAMESPACE + "CONFIGURATION";
28 |
29 | public static final String CATEGORY_ALARM = NAMESPACE + ".CATEGORY_ALARM";
30 |
31 | public static final String SCHEME = "prevent";
32 | public static final String PERMISSION_MANAGER = Manifest.permission.MANAGER;
33 | public static final String PERMISSION_SYSTEM = "android.permission.SHUTDOWN";
34 |
35 | public static final String KEY_FORCE_STOP_TIMEOUT = "force_stop_timeout";
36 | public static final String KEY_DESTROY_PROCESSES = "destroy_processes";
37 | public static final String KEY_BACKUP_PREVENT_LIST = "backup_prevent_list";
38 | public static final String KEY_LOCK_SYNC_SETTINGS = "lock_sync_settings";
39 | public static final String KEY_AUTO_PREVENT = "auto_prevent";
40 | public static final String KEY_STOP_SIGNATURE_APPS = "stop_signature_apps";
41 | public static final String KEY_USE_APP_STANDBY = "use_app_standby";
42 | public static final String KEY_ALLOW_EMPTY_SENDER = "allow_empty_sender";
43 | public static final String KEY_PREVENT_LIST = "prevent_list";
44 |
45 | public static final String KEY_OPTIMIZE_AUDIO = "optimize_audio";
46 | public static final String KEY_OPTIMIZE_VPN = "optimize_vpn";
47 | public static final String[] KEYS_LONG = new String[] {
48 | KEY_FORCE_STOP_TIMEOUT
49 | };
50 |
51 | public static final String[] KEYS_BOOLEAN = new String[] {
52 | KEY_ALLOW_EMPTY_SENDER,
53 | KEY_DESTROY_PROCESSES,
54 | KEY_LOCK_SYNC_SETTINGS,
55 | KEY_AUTO_PREVENT,
56 | KEY_STOP_SIGNATURE_APPS,
57 | KEY_USE_APP_STANDBY,
58 | KEY_OPTIMIZE_AUDIO,
59 | KEY_OPTIMIZE_VPN
60 | };
61 |
62 | public static final Uri CONTENT_URI = Uri.parse("content://me.piebridge.prevent.provider");
63 | public static final String LOGCAT_BOOT = "boot";
64 | public static final String LOGCAT_COMPLETED = "completed";
65 |
66 | private PreventIntent() {
67 |
68 | }
69 |
70 | private static boolean isKey(String key, String[] keys) {
71 | for (String k : keys) {
72 | if (k.equals(key)) {
73 | return true;
74 | }
75 | }
76 | return false;
77 | }
78 |
79 | public static boolean isBoolean(String key) {
80 | return isKey(key, KEYS_BOOLEAN);
81 | }
82 |
83 | public static boolean isLong(String key) {
84 | return isKey(key, KEYS_LONG);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/common/TimeUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.common;
2 |
3 | import android.app.ActivityManager;
4 | import android.os.SystemClock;
5 |
6 | import java.util.LinkedHashSet;
7 | import java.util.Set;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * Created by thom on 2016/10/20.
12 | */
13 | public class TimeUtils {
14 |
15 | private static long MAX_IMPORTANCE = 0;
16 | private static final Set IMPORTANCES = new LinkedHashSet();
17 |
18 | static {
19 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND);
20 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY);
21 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
22 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
23 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE);
24 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE);
25 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
26 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING);
27 | IMPORTANCES.add((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE);
28 | for (Long importance : IMPORTANCES) {
29 | if (importance > MAX_IMPORTANCE) {
30 | MAX_IMPORTANCE = importance;
31 | }
32 | }
33 | }
34 |
35 | private TimeUtils() {
36 |
37 | }
38 |
39 | public static long now() {
40 | return TimeUnit.MILLISECONDS.toSeconds(SystemClock.elapsedRealtime());
41 | }
42 |
43 | public static long fixImportance(long time) {
44 | if (time > MAX_IMPORTANCE) {
45 | return time;
46 | }
47 | long fixed = time;
48 | while (IMPORTANCES.contains(fixed)) {
49 | fixed += 1;
50 | }
51 | return fixed;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/CheckingRunningService.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 |
6 | import java.util.Collection;
7 | import java.util.Collections;
8 | import java.util.Map;
9 | import java.util.Set;
10 | import java.util.TreeSet;
11 |
12 | import me.piebridge.prevent.BuildConfig;
13 | import me.piebridge.prevent.common.Configuration;
14 | import me.piebridge.prevent.common.PackageUtils;
15 | import me.piebridge.prevent.framework.util.HookUtils;
16 |
17 | /**
18 | * Created by thom on 15/7/25.
19 | */
20 |
21 | abstract class CheckingRunningService implements Runnable {
22 |
23 | private final Context mContext;
24 | private Map mPreventPackages;
25 |
26 | CheckingRunningService(Context context, Map preventPackages) {
27 | mContext = context;
28 | mPreventPackages = preventPackages;
29 | }
30 |
31 | @Override
32 | public void run() {
33 | Collection packageNames = preparePackageNames();
34 | Collection whiteList = prepareWhiteList();
35 | if (!packageNames.isEmpty() && packageNames.equals(whiteList)) {
36 | return;
37 | }
38 | PreventLog.d("checking services, packages: " + packageNames + ", whitelist: " + whiteList);
39 | Set shouldStopPackageNames = new TreeSet();
40 | if (Configuration.getDefault().isDestroyProcesses()) {
41 | shouldStopPackageNames.addAll(packageNames);
42 | shouldStopPackageNames.removeAll(whiteList);
43 | packageNames = Collections.emptyList();
44 | }
45 | for (ActivityManager.RunningServiceInfo service : HookUtils.getServices(mContext)) {
46 | checkService(service, packageNames, whiteList, shouldStopPackageNames);
47 | }
48 | stopServiceIfNeeded(shouldStopPackageNames);
49 | PreventLog.v("complete checking running service");
50 | }
51 |
52 | private boolean checkService(ActivityManager.RunningServiceInfo service, Collection packageNames, Collection whiteList, Set shouldStopPackageNames) {
53 | String name = service.service.getPackageName();
54 | boolean prevent = Boolean.TRUE.equals(mPreventPackages.get(name));
55 | logServiceIfNeeded(prevent, name, service);
56 | if (!prevent || whiteList.contains(name)) {
57 | return false;
58 | }
59 | if (packageNames.contains(name) || service.started) {
60 | shouldStopPackageNames.add(name);
61 | }
62 | return true;
63 | }
64 |
65 | protected abstract Collection preparePackageNames();
66 |
67 | protected abstract Collection prepareWhiteList();
68 |
69 | private void logServiceIfNeeded(boolean prevents, String name, ActivityManager.RunningServiceInfo service) {
70 | if (!service.started) {
71 | return;
72 | }
73 | if (BuildConfig.DEBUG || prevents || service.uid >= PackageUtils.FIRST_APPLICATION_UID) {
74 | PreventLog.v("prevents: " + prevents + ", name: " + name + ", count: " + service.clientCount + ", label: " + service.clientLabel
75 | + ", uid: " + service.uid + ", pid: " + service.pid + ", process: " + service.process + ", flags: " + service.flags);
76 | }
77 | }
78 |
79 | private void stopServiceIfNeeded(Set shouldStopPackageNames) {
80 | for (String name : shouldStopPackageNames) {
81 | String forceStop = "force stop";
82 | if (SystemHook.isUseAppStandby()) {
83 | forceStop = "standby";
84 | }
85 | if (Configuration.getDefault().isDestroyProcesses()) {
86 | PreventLog.i(forceStop + " " + name);
87 | } else {
88 | PreventLog.i(name + " has running services, " + forceStop + " it");
89 | }
90 | SystemHook.forceStopPackageIfNeeded(name);
91 | }
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/IntentFilterMatchResult.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework;
2 |
3 | import android.content.IntentFilter;
4 |
5 | /**
6 | * Created by thom on 15/7/12.
7 | */
8 | public final class IntentFilterMatchResult {
9 |
10 | private Class> type;
11 | private Integer result;
12 |
13 | public static final IntentFilterMatchResult NONE = new IntentFilterMatchResult(Void.class, null);
14 | public static final IntentFilterMatchResult NO_MATCH = new IntentFilterMatchResult(int.class, IntentFilter.NO_MATCH_ACTION);
15 |
16 | private IntentFilterMatchResult(Class> type, Integer result) {
17 | this.type = type;
18 | this.result = result;
19 | }
20 |
21 | public boolean isNone() {
22 | return Void.class.equals(this.type);
23 | }
24 |
25 | public Integer getResult() {
26 | return this.result;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/PreventLog.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by thom on 15/7/25.
7 | */
8 | public class PreventLog {
9 |
10 | public static final String TAG = "Prevent";
11 |
12 | private PreventLog() {
13 |
14 | }
15 |
16 | public static void v(String msg) {
17 | Log.v(TAG, msg);
18 | }
19 |
20 | public static void v(String msg, Throwable t) {
21 | Log.v(TAG, msg, t);
22 | }
23 |
24 | public static void d(String msg) {
25 | Log.d(TAG, msg);
26 | }
27 |
28 | public static void d(String msg, Throwable t) {
29 | Log.d(TAG, msg, t);
30 | }
31 |
32 | public static void i(String msg) {
33 | Log.i(TAG, msg);
34 | }
35 |
36 | public static void w(String msg) {
37 | Log.w(TAG, msg);
38 | }
39 |
40 | public static void w(String msg, Throwable t) {
41 | Log.w(TAG, msg, t);
42 | }
43 |
44 | public static void e(String msg) {
45 | Log.e(TAG, msg);
46 | }
47 |
48 | public static void e(String msg, Throwable t) {
49 | Log.e(TAG, msg, t);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/ActivityRecordUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.content.pm.ActivityInfo;
4 | import android.content.pm.ApplicationInfo;
5 |
6 | import java.lang.ref.WeakReference;
7 | import java.lang.reflect.Field;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | import me.piebridge.prevent.framework.PreventLog;
12 | import me.piebridge.prevent.framework.SystemHook;
13 |
14 | /**
15 | * Created by thom on 15/9/18.
16 | */
17 | public class ActivityRecordUtils {
18 |
19 | private static Field weakActivity;
20 | private static Class weakActivityClass;
21 |
22 | private static Map fields = new HashMap();
23 | private static Class fieldsClass;
24 |
25 | private ActivityRecordUtils() {
26 |
27 | }
28 |
29 | public static boolean isActivityRecord(Object object) {
30 | return object != null && object.getClass().getSimpleName().endsWith("ActivityRecord");
31 | }
32 |
33 | public static boolean isActivityStack(Object object) {
34 | return object != null && object.getClass().getSimpleName().endsWith("ActivityStack");
35 | }
36 | private static Object getField(Object target, String name) {
37 | Object activityRecord = getActivityRecord(target);
38 | if (activityRecord == null) {
39 | PreventLog.e("cannot find activity record from " + target);
40 | return null;
41 | }
42 | Field field = getCacheField(target, name);
43 | if (field != null) {
44 | try {
45 | return field.get(activityRecord);
46 | } catch (IllegalAccessException e) {
47 | PreventLog.e("cannot access " + name + " in " + activityRecord, e);
48 | }
49 | } else {
50 | PreventLog.e("cannot get " + name + " in " + activityRecord);
51 | }
52 | return null;
53 | }
54 |
55 | private static Field getCacheField(Object target, String name) {
56 | if (target == null) {
57 | return null;
58 | }
59 | Field field;
60 | if (fieldsClass == target.getClass() && fields.containsKey(name)) {
61 | field = fields.get(name);
62 | } else {
63 | fieldsClass = target.getClass();
64 | field = ReflectUtils.getDeclaredField(target, name);
65 | if (field == null) {
66 | SystemHook.setNotSupported();
67 | PreventLog.e("cannot find " + name + " in " + fieldsClass);
68 | } else {
69 | PreventLog.d("find " + name + " " + field + " in " + fieldsClass);
70 | }
71 | fields.put(name, field);
72 | }
73 | return field;
74 | }
75 |
76 | public static Object getActivityRecord(Object target) {
77 | if (isActivityRecord(target)) {
78 | return target;
79 | }
80 | Field field = getCacheField(target);
81 | if (field != null) {
82 | try {
83 | return ((WeakReference>) field.get(target)).get();
84 | } catch (IllegalAccessException e) {
85 | PreventLog.e("cannot access weakActivity in " + target, e);
86 | }
87 | } else {
88 | PreventLog.e("cannot get weakActivity in " + target);
89 | }
90 | return null;
91 | }
92 |
93 | private static Field getCacheField(Object target) {
94 | if (target == null) {
95 | return null;
96 | }
97 | if (weakActivityClass != target.getClass()) {
98 | weakActivityClass = target.getClass();
99 | weakActivity = ReflectUtils.getDeclaredField(target, "weakActivity");
100 | if (weakActivity == null) {
101 | PreventLog.e("cannot find weakActivity in " + weakActivityClass);
102 | SystemHook.setNotSupported();
103 | } else {
104 | PreventLog.d("find weakActivity " + weakActivity + " in " + weakActivityClass);
105 | }
106 | }
107 | return weakActivity;
108 | }
109 |
110 | public static Object getTask(Object target) {
111 | return getField(target, "task");
112 | }
113 |
114 | public static String getPackageName(Object target) {
115 | return (String) getField(target, "packageName");
116 | }
117 |
118 | public static ActivityInfo getInfo(Object target) {
119 | return (ActivityInfo) getField(target, "info");
120 | }
121 |
122 | public static int getPid(Object target) {
123 | Object processRecord = getField(target, "app");
124 | return ProcessRecordUtils.getPid(processRecord);
125 | }
126 |
127 | public static int getUid(Object target) {
128 | Object processRecord = getField(target, "app");
129 | ApplicationInfo info = ProcessRecordUtils.getInfo(processRecord);
130 | if (info == null) {
131 | return 0;
132 | } else {
133 | return info.uid;
134 | }
135 | }
136 |
137 | public static String getActivityName(Object target) {
138 | return (String) getField(target, "stringName");
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/AlarmManagerServiceUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import java.util.Set;
7 |
8 | import me.piebridge.prevent.common.PreventIntent;
9 | import me.piebridge.prevent.framework.IntentFilterMatchResult;
10 | import me.piebridge.prevent.framework.PreventLog;
11 |
12 | /**
13 | * Created by thom on 15/8/2.
14 | */
15 | public class AlarmManagerServiceUtils {
16 |
17 | private static Object cachedFilter;
18 |
19 | private AlarmManagerServiceUtils() {
20 |
21 | }
22 |
23 | public static void releaseAlarm(Context context, String packageName) {
24 | final Intent intent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE, null);
25 | intent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[]{packageName});
26 | intent.addCategory(PreventIntent.CATEGORY_ALARM);
27 | intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
28 | context.sendBroadcast(intent);
29 | }
30 |
31 | public static boolean canHook(Set categories) {
32 | return categories != null && categories.contains(PreventIntent.CATEGORY_ALARM);
33 | }
34 |
35 | public static IntentFilterMatchResult hook(Object filter) {
36 | if (isAlarm(filter)) {
37 | return IntentFilterMatchResult.NONE;
38 | } else {
39 | return IntentFilterMatchResult.NO_MATCH;
40 | }
41 | }
42 |
43 | private static boolean isAlarm(Object filter) {
44 | if (cachedFilter != null) {
45 | return cachedFilter.equals(filter);
46 | }
47 | String receiverName = BroadcastFilterUtils.getReceiverName(filter);
48 | if (receiverName != null && receiverName.endsWith("AlarmManagerService$UninstallReceiver")) {
49 | PreventLog.d("found " + receiverName + " in filter: " + filter);
50 | cachedFilter = filter;
51 | return true;
52 | } else {
53 | PreventLog.v("checking " + receiverName + " in filter: " + filter);
54 | return false;
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/BroadcastFilterUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.content.pm.ApplicationInfo;
4 |
5 | import java.lang.ref.WeakReference;
6 | import java.lang.reflect.Field;
7 |
8 | import me.piebridge.prevent.framework.PreventLog;
9 | import me.piebridge.prevent.framework.SystemHook;
10 |
11 | /**
12 | * Created by thom on 15/7/14.
13 | */
14 | public class BroadcastFilterUtils {
15 |
16 | private static Class> BroadcastFilter;
17 | private static Field BroadcastFilter$receiverList;
18 | private static Field ReceiverList$app;
19 | private static Field ReceiverList$receiver;
20 |
21 | private BroadcastFilterUtils() {
22 |
23 | }
24 |
25 | static {
26 | initReflections();
27 | }
28 |
29 | private static void initReflections() {
30 | PreventLog.d("init BroadcastFilterUtils");
31 | ClassLoader classLoader = SystemHook.getClassLoader();
32 | try {
33 | BroadcastFilter = Class.forName("com.android.server.am.BroadcastFilter", false, classLoader);
34 | BroadcastFilter$receiverList = BroadcastFilter.getDeclaredField("receiverList");
35 | BroadcastFilter$receiverList.setAccessible(true);
36 |
37 | Class> receiverList = Class.forName("com.android.server.am.ReceiverList", false, classLoader);
38 | ReceiverList$app = receiverList.getDeclaredField("app");
39 | ReceiverList$app.setAccessible(true);
40 |
41 | ReceiverList$receiver = receiverList.getDeclaredField("receiver");
42 | ReceiverList$receiver.setAccessible(true);
43 | } catch (ClassNotFoundException e) {
44 | PreventLog.e("cannot find classes for BroadcastFilterUtils", e);
45 | SystemHook.setNotSupported();
46 | } catch (NoSuchFieldException e) {
47 | PreventLog.e("cannot find fields for BroadcastFilterUtils", e);
48 | SystemHook.setNotSupported();
49 | }
50 | }
51 |
52 | public static boolean isBroadcastFilter(Object filter) {
53 | return ReceiverList$receiver != null && BroadcastFilter.isAssignableFrom(filter.getClass());
54 | }
55 |
56 | public static String getPackageName(Object filter) {
57 | if (!isBroadcastFilter(filter)) {
58 | return null;
59 | }
60 | try {
61 | Object receiverList = BroadcastFilter$receiverList.get(filter);
62 | Object app = ReceiverList$app.get(receiverList);
63 | ApplicationInfo info = ProcessRecordUtils.getInfo(app);
64 | if (info != null) {
65 | return info.packageName;
66 | }
67 | } catch (IllegalAccessException e) {
68 | PreventLog.e("cannot get package name from " + filter, e);
69 | }
70 | return null;
71 | }
72 |
73 | public static String getReceiverName(Object filter) {
74 | if (!isBroadcastFilter(filter)) {
75 | return null;
76 | }
77 | try {
78 | Object receiverList = BroadcastFilter$receiverList.get(filter);
79 | Object receiver = ReceiverList$receiver.get(receiverList);
80 | Field field = receiver.getClass().getDeclaredField("mDispatcher");
81 | field.setAccessible(true);
82 | WeakReference mDispatcher = (WeakReference) field.get(receiver);
83 | Object rd = mDispatcher.get();
84 | field = rd.getClass().getDeclaredField("mReceiver");
85 | field.setAccessible(true);
86 | return field.get(rd).getClass().getName();
87 | } catch (NoSuchFieldException e) {
88 | PreventLog.v("cannot find field for filter: " + filter, e);
89 | } catch (IllegalAccessException e) {
90 | PreventLog.d("cannot access field for filter: " + filter, e);
91 | } catch (NullPointerException e) {
92 | PreventLog.v("cannot get field for filter: " + filter, e);
93 | }
94 | return null;
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/HideApiUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.os.Process;
7 |
8 | import java.lang.reflect.Field;
9 |
10 | import me.piebridge.prevent.framework.PreventLog;
11 |
12 | public class HideApiUtils {
13 |
14 | private HideApiUtils() {
15 |
16 | }
17 |
18 | public static int getUidForPid(int pid) {
19 | return Process.getUidForPid(pid);
20 | }
21 |
22 | public static int getParentPid(int pid) {
23 | String[] procStatusLabels = {"PPid:"};
24 | long[] procStatusValues = new long[1];
25 | procStatusValues[0] = -1;
26 | Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
27 | return (int) procStatusValues[0];
28 | }
29 |
30 | public static void forceStopPackage(Context context, String packageName) {
31 | try {
32 | PreventLog.e("trying to force stop package" + packageName);
33 | PackageManager pm = context.getPackageManager();
34 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
35 | activityManager.forceStopPackage(packageName);
36 | } catch (Throwable t) {
37 | t.printStackTrace();
38 | PreventLog.e("cannot force stop package" + packageName, t);
39 | }
40 | }
41 |
42 | public static Object getThis0(Object object) {
43 | if (object == null) {
44 | return null;
45 | }
46 | Class> clazz = object.getClass();
47 | try {
48 | Field field = clazz.getDeclaredField("this$0");
49 | field.setAccessible(true);
50 | return field.get(object);
51 | } catch (NoSuchFieldException e) {
52 | PreventLog.d("cannot find this$0 in class: " + clazz, e);
53 | } catch (IllegalAccessException e) {
54 | PreventLog.d("cannot visit this$0 in class: " + clazz, e);
55 | }
56 | return null;
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/HookUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 |
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 | import me.piebridge.prevent.framework.PreventLog;
10 |
11 | /**
12 | * Created by thom on 15/8/2.
13 | */
14 | public class HookUtils {
15 |
16 | private HookUtils() {
17 |
18 | }
19 |
20 | public static List getServices(Context context) {
21 | if (context == null) {
22 | return Collections.emptyList();
23 | }
24 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
25 | List services = activityManager.getRunningServices(Integer.MAX_VALUE);
26 | if (services != null) {
27 | PreventLog.v("services size: " + services.size());
28 | return services;
29 | } else {
30 | return Collections.emptyList();
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/LogcatUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.net.Uri;
6 | import android.util.Base64;
7 |
8 | import java.io.BufferedInputStream;
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.IOException;
12 | import java.io.InputStream;
13 | import java.text.SimpleDateFormat;
14 | import java.util.Date;
15 | import java.util.Locale;
16 |
17 | import me.piebridge.prevent.common.PreventIntent;
18 | import me.piebridge.prevent.framework.PreventLog;
19 |
20 | /**
21 | * Created by thom on 15/8/11.
22 | */
23 | public class LogcatUtils {
24 |
25 | private static final String CACHE_PREFIX = "/data/system/me.piebridge.prevent.log.";
26 | private static final String COMMAND = "/system/bin/logcat -d -v time -f " + CACHE_PREFIX;
27 |
28 | public static final String BOOT = PreventIntent.LOGCAT_BOOT;
29 | public static final String PREVENT = "prevent";
30 | public static final String SYSTEM = "system";
31 | public static final String COMPLETED = "completed";
32 |
33 | private LogcatUtils() {
34 |
35 | }
36 |
37 | public static void logcat(String prefix, String log) {
38 | File cache = new File(CACHE_PREFIX + prefix);
39 | if (cache.exists()) {
40 | cache.delete();
41 | }
42 | try {
43 | String command = COMMAND + prefix + " " + log;
44 | PreventLog.d("will execute: " + command);
45 | Runtime.getRuntime().exec(command).waitFor();
46 | PreventLog.d("execute complete: " + command);
47 | } catch (InterruptedException e) {
48 | PreventLog.e("execute interrupted", e);
49 | } catch (IOException e) {
50 | PreventLog.d("exec wrong", e);
51 | }
52 | }
53 |
54 | public static long logcat(Context context, String prefix) {
55 | File cache = new File(CACHE_PREFIX + prefix);
56 | if (cache.exists()) {
57 | long size = cache.length();
58 | PreventLog.d("send " + prefix + " log, size: " + cache.length());
59 | try {
60 | sendToUi(context, new BufferedInputStream(new FileInputStream(cache)), prefix);
61 | PreventLog.d("send to ui successfully");
62 | } catch (IOException e) {
63 | PreventLog.d("cannot send log to ui", e);
64 | }
65 | cache.delete();
66 | return size;
67 | } else if (!BOOT.equals(prefix)) {
68 | PreventLog.d("not exist: " + cache.getAbsolutePath());
69 | }
70 | return 0L;
71 | }
72 |
73 | private static void sendToUi(Context context, InputStream is, String prefix) throws IOException {
74 | int length;
75 | byte[] buffer = new byte[0x300];
76 | ContentResolver contentResolver = context.getContentResolver();
77 | String path = new SimpleDateFormat("yyyyMMdd.HH.mm.ss'.txt'", Locale.US).format(new Date());
78 | int offset = 0;
79 | while ((length = is.read(buffer)) != -1) {
80 | String line = Base64.encodeToString(buffer, 0, length, Base64.URL_SAFE | Base64.NO_WRAP);
81 | Uri uri = PreventIntent.CONTENT_URI.buildUpon().appendQueryParameter("path", prefix + "." + path)
82 | .appendQueryParameter("offset", String.valueOf(offset))
83 | .appendQueryParameter("log", line).build();
84 | contentResolver.query(uri, null, null, null, null);
85 | offset += length;
86 | }
87 | is.close();
88 | }
89 |
90 | public static void completed(Context context) {
91 | ContentResolver contentResolver = context.getContentResolver();
92 | Uri uri = PreventIntent.CONTENT_URI.buildUpon().appendQueryParameter("path", COMPLETED)
93 | .appendQueryParameter("offset", String.valueOf(0))
94 | .appendQueryParameter("log", "").build();
95 | contentResolver.query(uri, null, null, null, null);
96 | }
97 |
98 | public static void deleteBootLog() {
99 | File cache = new File(CACHE_PREFIX + BOOT);
100 | if (cache.exists()) {
101 | cache.delete();
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/ProcessRecordUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.content.pm.ApplicationInfo;
4 | import android.os.Build;
5 |
6 | import java.lang.reflect.Field;
7 |
8 | import me.piebridge.prevent.framework.PreventLog;
9 | import me.piebridge.prevent.framework.SystemHook;
10 |
11 | /**
12 | * Created by thom on 15/7/14.
13 | */
14 | public class ProcessRecordUtils {
15 |
16 | private static Class> ProcessRecord;
17 |
18 | private static Field ProcessRecord$info;
19 |
20 | private static Field ProcessRecord$pid;
21 |
22 | private static Field ProcessRecord$killedByAm;
23 |
24 | private ProcessRecordUtils() {
25 |
26 | }
27 |
28 | static {
29 | initReflection();
30 | }
31 |
32 | public static void initReflection() {
33 | PreventLog.d("init ProcessRecordUtils");
34 | ClassLoader classLoader = SystemHook.getClassLoader();
35 | try {
36 | ProcessRecord = Class.forName("com.android.server.am.ProcessRecord", false, classLoader);
37 | ProcessRecord$info = ProcessRecord.getDeclaredField("info");
38 | ProcessRecord$info.setAccessible(true);
39 |
40 | ProcessRecord$pid = ProcessRecord.getDeclaredField("pid");
41 | ProcessRecord$pid.setAccessible(true);
42 |
43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
44 | ProcessRecord$killedByAm = ProcessRecord.getDeclaredField("killedByAm");
45 | } else {
46 | ProcessRecord$killedByAm = ProcessRecord.getDeclaredField("killedBackground");
47 | }
48 | ProcessRecord$killedByAm.setAccessible(true);
49 | } catch (ClassNotFoundException e) {
50 | PreventLog.e("cannot find class for ProcessRecordUtils", e);
51 | SystemHook.setNotSupported();
52 | } catch (NoSuchFieldException e) {
53 | PreventLog.e("cannot find fields for ProcessRecordUtils", e);
54 | SystemHook.setNotSupported();
55 | }
56 | }
57 |
58 | public static ApplicationInfo getInfo(Object pr) {
59 | if (pr == null || ProcessRecord$info == null || !ProcessRecord.isAssignableFrom(pr.getClass())) {
60 | return null;
61 | }
62 | try {
63 | return (ApplicationInfo) ProcessRecord$info.get(pr);
64 | } catch (IllegalAccessException e) {
65 | PreventLog.e("cannot get info", e);
66 | return null;
67 | }
68 | }
69 |
70 | public static int getPid(Object pr) {
71 | if (pr == null || ProcessRecord$pid == null || !ProcessRecord.isAssignableFrom(pr.getClass())) {
72 | return 0;
73 | }
74 | try {
75 | return (Integer) ProcessRecord$pid.get(pr);
76 | } catch (IllegalAccessException e) {
77 | PreventLog.e("cannot get pid", e);
78 | return 0;
79 | }
80 | }
81 |
82 | public static boolean isKilledByAm(Object pr) {
83 | if (pr == null || ProcessRecord$killedByAm == null || !ProcessRecord.isAssignableFrom(pr.getClass())) {
84 | return true;
85 | }
86 | try {
87 | return (Boolean) ProcessRecord$killedByAm.get(pr);
88 | } catch (IllegalAccessException e) {
89 | PreventLog.e("cannot get killedByAm", e);
90 | return true;
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/ReflectUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import java.lang.reflect.Field;
4 | import java.lang.reflect.InvocationTargetException;
5 | import java.lang.reflect.Method;
6 | import java.util.LinkedHashMap;
7 | import java.util.Map;
8 |
9 | import me.piebridge.prevent.framework.PreventLog;
10 |
11 | /**
12 | * Created by thom on 16/2/3.
13 | */
14 | public class ReflectUtils {
15 |
16 | private static final Map METHOD_CACHES = new LinkedHashMap();
17 |
18 | private ReflectUtils() {
19 |
20 | }
21 |
22 | public static Field getDeclaredField(Object target, String name) {
23 | if (target == null) {
24 | return null;
25 | }
26 | Field field = null;
27 | Class clazz = target.getClass();
28 | while (clazz != null&&field==null) {
29 | try {
30 | field = clazz.getField(name);
31 | } catch (NoSuchFieldException e) {
32 | PreventLog.d("cannot find field " + name + " in " + clazz);
33 | clazz = clazz.getSuperclass();
34 | }
35 | }
36 | if (field == null) {
37 | PreventLog.e("cannot find field " + name + " in " + target.getClass());
38 | } else {
39 | field.setAccessible(true);
40 | }
41 | return field;
42 | }
43 |
44 | public static Object invoke(Object target, String name) {
45 | String key = target.getClass() + "#" + name;
46 | Method method = METHOD_CACHES.get(key);
47 | try {
48 | if (method == null) {
49 | method = getMethod(target, name);
50 | method.setAccessible(true);
51 | METHOD_CACHES.put(key, method);
52 | }
53 | return method.invoke(target, (Object[]) null);
54 | } catch (NoSuchMethodException e) {
55 | PreventLog.e("cannot find method " + name + " in " + target.getClass());
56 | } catch (InvocationTargetException e) {
57 | PreventLog.e("cannot invoke " + method + " in " + target.getClass());
58 | } catch (IllegalAccessException e) {
59 | PreventLog.e("cannot access " + method + " in " + target.getClass());
60 | }
61 | return null;
62 | }
63 |
64 | public static Object invoke(Object target, String name, Class>[] parameterTypes, Object[] args) {
65 | String key = target.getClass() + "#" + name;
66 | Method method = METHOD_CACHES.get(key);
67 | try {
68 | if (method == null) {
69 | method = getMethod(target, name, parameterTypes);
70 | method.setAccessible(true);
71 | METHOD_CACHES.put(key, method);
72 | }
73 | return method.invoke(target, args);
74 | } catch (NoSuchMethodException e) {
75 | PreventLog.e("cannot find method " + name + " in " + target.getClass());
76 | } catch (InvocationTargetException e) {
77 | PreventLog.e("cannot invoke " + method + " in " + target.getClass());
78 | } catch (IllegalAccessException e) {
79 | PreventLog.e("cannot access " + method + " in " + target.getClass());
80 | }
81 | return null;
82 | }
83 |
84 | private static Method getMethod(Object target, String name) throws NoSuchMethodException {
85 | for (Method m : target.getClass().getMethods()) {
86 | if (m.getName().equals(name)) {
87 | return m;
88 | }
89 | }
90 | Class superClass = target.getClass().getSuperclass();
91 | while (superClass != null) {
92 | for (Method m : target.getClass().getMethods()) {
93 | if (m.getName().equals(name)) {
94 | return m;
95 | }
96 | }
97 | superClass = superClass.getSuperclass();
98 | }
99 | throw new NoSuchMethodException();
100 | }
101 |
102 | private static Method getMethod(Object target, String name, Class>[] parameterTypes) throws NoSuchMethodException {
103 | try {
104 | return target.getClass().getMethod(name, parameterTypes);
105 | } catch (NoSuchMethodException e) {
106 | Class> superClass = target.getClass().getSuperclass();
107 | while (superClass != null) {
108 | try {
109 | return superClass.getMethod(name, parameterTypes);
110 | } catch (NoSuchMethodException e1) {
111 | superClass = superClass.getSuperclass();
112 | }
113 | }
114 | }
115 | throw new NoSuchMethodException();
116 | }
117 |
118 | public static Object get(Object target, String name) {
119 | Field field=null;
120 | try {
121 | field = getDeclaredField(target, name);
122 | field.setAccessible(true);
123 | return field.get(target);
124 | } catch (Exception e) {
125 | PreventLog.e("cannot access " + field + " in " + target.getClass());
126 | }
127 | return null;
128 | }
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/ResourcesUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.content.res.Resources;
4 |
5 | import me.piebridge.prevent.BuildConfig;
6 |
7 | /**
8 | * Created by thom on 2016/10/16.
9 | */
10 |
11 | public class ResourcesUtils {
12 |
13 | private ResourcesUtils() {
14 |
15 | }
16 |
17 | public static String getString(Resources resources, String identifier) {
18 | int resId = resources.getIdentifier(identifier, "string", BuildConfig.APPLICATION_ID);
19 | if (resId != 0) {
20 | return resources.getString(resId);
21 | } else {
22 | return "(" + identifier + ")";
23 | }
24 | }
25 |
26 | public static String formatString(Resources resources, String identifier, int size) {
27 | int resId = resources.getIdentifier(identifier, "string", BuildConfig.APPLICATION_ID);
28 | if (resId != 0) {
29 | return resources.getString(resId, size);
30 | } else {
31 | return "(" + identifier + ", " + size + ")";
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/framework/util/TaskRecordUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.framework.util;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Intent;
5 |
6 | import java.lang.reflect.Field;
7 |
8 | import me.piebridge.prevent.framework.PreventLog;
9 | import me.piebridge.prevent.framework.SystemHook;
10 |
11 | /**
12 | * Created by thom on 15/7/23.
13 | */
14 | public class TaskRecordUtils {
15 |
16 | private static Field taskRecord$intent;
17 |
18 | private static Field taskRecord$affinityIntent;
19 |
20 | private static Class taskRecordClass;
21 |
22 | private TaskRecordUtils() {
23 |
24 | }
25 |
26 | public static String getPackageName(Object object) {
27 | try {
28 | Intent intent = getIntent(object);
29 | if (intent == null) {
30 | return null;
31 | }
32 | ComponentName cn = intent.getComponent();
33 | if (cn != null) {
34 | return cn.getPackageName();
35 | }
36 | } catch (IllegalAccessException e) {
37 | PreventLog.e("cannot get field value in TaskRecord", e);
38 | }
39 | return null;
40 | }
41 |
42 | private static Intent getIntent(Object object) throws IllegalAccessException {
43 | Object taskRecord;
44 | if (ActivityRecordUtils.isActivityRecord(object)) {
45 | taskRecord = ActivityRecordUtils.getTask(object);
46 | } else {
47 | taskRecord = object;
48 | }
49 | if (taskRecord == null) {
50 | return null;
51 | }
52 | if (taskRecordClass != taskRecord.getClass()) {
53 | taskRecordClass = taskRecord.getClass();
54 | taskRecord$intent = ReflectUtils.getDeclaredField(taskRecord, "intent");
55 | taskRecord$affinityIntent = ReflectUtils.getDeclaredField(taskRecord, "affinityIntent");
56 | if (taskRecord$intent == null || taskRecord$affinityIntent == null) {
57 | SystemHook.setNotSupported();
58 | return null;
59 | }
60 | }
61 | Intent intent = (Intent) taskRecord$intent.get(taskRecord);
62 | if (intent == null) {
63 | intent = (Intent) taskRecord$affinityIntent.get(taskRecord);
64 | }
65 | return intent;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/AdvancedSettingsActivity.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.os.Bundle;
4 |
5 | import androidx.appcompat.app.AppCompatActivity;
6 | import androidx.appcompat.widget.Toolbar;
7 |
8 | import me.piebridge.prevent.R;
9 |
10 | /**
11 | * Created by thom on 15/10/3.
12 | */
13 | public class AdvancedSettingsActivity extends AppCompatActivity {
14 |
15 |
16 | @Override
17 | public void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_settings);
20 | Toolbar toolbar=findViewById(R.id.activitysettingsToolbar1);
21 | setSupportActionBar(toolbar);
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/Applications.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.content.pm.ApplicationInfo;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.graphics.drawable.Drawable;
7 |
8 | import java.util.HashSet;
9 | import java.util.Map;
10 | import java.util.Set;
11 | import java.util.TreeSet;
12 |
13 | import me.piebridge.prevent.common.PackageUtils;
14 | import me.piebridge.prevent.ui.util.LabelLoader;
15 |
16 | /**
17 | * Created by thom on 16/2/17.
18 | */
19 | public class Applications extends PreventFragment {
20 |
21 | @Override
22 | protected Set getPackageNames() {
23 | Set names = new HashSet();
24 | PackageManager pm = getActivity().getPackageManager();
25 | for (PackageInfo pkgInfo : pm.getInstalledPackages(0)) {
26 | ApplicationInfo appInfo = pkgInfo.applicationInfo;
27 | if (!PackageUtils.isSystemPackage(appInfo.flags)) {
28 | names.add(appInfo.packageName);
29 | }
30 | }
31 | return names;
32 | }
33 |
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/ListPreferenceSummary.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.content.Context;
4 | import androidx.preference.ListPreference;
5 | import android.util.AttributeSet;
6 |
7 | /**
8 | * Created by thom on 15/10/3.
9 | */
10 | public class ListPreferenceSummary extends ListPreference {
11 |
12 | private OnPreferenceClickListener mOnClickListener;
13 |
14 | public ListPreferenceSummary(Context context) {
15 | super(context);
16 | }
17 |
18 | public ListPreferenceSummary(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | @Override
23 | public CharSequence getSummary() {
24 | CharSequence entry = getEntry();
25 | if (getEntries()[0].equals(entry)) {
26 | return entry;
27 | }
28 | return super.getSummary();
29 | }
30 |
31 | @Override
32 | public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
33 | mOnClickListener = onPreferenceClickListener;
34 | }
35 |
36 | @Override
37 | public void onClick() {
38 | if (mOnClickListener == null || !mOnClickListener.onPreferenceClick(this)) {
39 | super.onClick();
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/PreventProvider.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.content.ContentProvider;
4 | import android.content.ContentValues;
5 | import android.content.Context;
6 | import android.database.Cursor;
7 | import android.net.Uri;
8 | import android.util.Base64;
9 |
10 | import java.io.File;
11 | import java.io.FileOutputStream;
12 | import java.io.IOException;
13 |
14 | import me.piebridge.prevent.common.FileUtils;
15 | import me.piebridge.prevent.common.PreventIntent;
16 |
17 | /**
18 | * Created by thom on 15/7/18.
19 | */
20 | public class PreventProvider extends ContentProvider {
21 |
22 | @Override
23 | public boolean onCreate() {
24 | return true;
25 | }
26 |
27 | @Override
28 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
29 | String log = uri.getQueryParameter("log");
30 | if (log != null) {
31 | saveLog(uri, log);
32 | }
33 | return null;
34 | }
35 |
36 | private void saveLog(Uri uri, String log) {
37 | String path = uri.getQueryParameter("path");
38 | String offset = uri.getQueryParameter("offset");
39 | if (path == null) {
40 | path = "logcat.log";
41 | }
42 | Context context = getContext();
43 | if (context == null) {
44 | return;
45 | }
46 | if (path.startsWith(PreventIntent.LOGCAT_BOOT) && "0".equals(offset)) {
47 | FileUtils.eraseFiles(context.getExternalCacheDir());
48 | }
49 | File dir = context.getExternalCacheDir();
50 | if (dir == null) {
51 | UILog.d("cannot find external file");
52 | return;
53 | }
54 | File file = new File(dir, path);
55 | try {
56 | FileOutputStream fos = new FileOutputStream(file, true);
57 | fos.write(Base64.decode(log, Base64.URL_SAFE | Base64.NO_WRAP));
58 | fos.close();
59 | } catch (IOException e) {
60 | UILog.e("cannot save log", e);
61 | }
62 | }
63 |
64 | @Override
65 | public String getType(Uri uri) {
66 | return null;
67 | }
68 |
69 | @Override
70 | public Uri insert(Uri uri, ContentValues values) {
71 | return null;
72 | }
73 |
74 | @Override
75 | public int delete(Uri uri, String selection, String[] selectionArgs) {
76 | return 0;
77 | }
78 |
79 | @Override
80 | public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
81 | return 0;
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/ProgressAlertDialog.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.widget.ProgressBar;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.appcompat.app.AlertDialog;
9 |
10 | import me.piebridge.prevent.R;
11 |
12 | public class ProgressAlertDialog extends AlertDialog {
13 |
14 | public ProgressAlertDialog(@NonNull Context context) {
15 | super(context);
16 | setView(LayoutInflater.from(context).inflate(R.layout.progress_dialog_horizonal, null));
17 |
18 | }
19 |
20 |
21 | public void setMax(int max) {
22 | ProgressBar mPb = findViewById(R.id.dialog_progressbar);
23 | mPb.setMax(max);
24 | }
25 |
26 | public void setProgress(int progress) {
27 | ProgressBar mPb = findViewById(R.id.dialog_progressbar);
28 | mPb.setProgress(progress);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/ScreenSlidePagerAdapter.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 |
4 |
5 | import android.view.ViewGroup;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.fragment.app.Fragment;
9 | import androidx.fragment.app.FragmentManager;
10 | import androidx.fragment.app.FragmentPagerAdapter;
11 | import androidx.fragment.app.FragmentStatePagerAdapter;
12 |
13 | /**
14 | * Created by thom on 2016/10/26.
15 | */
16 | public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
17 |
18 | private final PreventFragment[] mFragments;
19 |
20 | public ScreenSlidePagerAdapter(FragmentManager fm) {
21 | super(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
22 | mFragments = new PreventFragment[2];
23 | }
24 |
25 | @NonNull
26 | @Override
27 | public Fragment getItem(int position) {
28 | if (position == 0) {
29 | return new Applications();
30 | } else if (position == 1) {
31 | return new SystemApplications();
32 | } else {
33 | return null;
34 | }
35 | }
36 |
37 | @Override
38 | public Object instantiateItem(ViewGroup container, int position) {
39 | PreventFragment fragment = (PreventFragment) super.instantiateItem(container, position);
40 | mFragments[position] = fragment;
41 | return fragment;
42 | }
43 |
44 | /*@Override
45 | public Fragment getItem(int position) {
46 | if (position == 0) {
47 | return new Applications();
48 | } else if (position == 1) {
49 | return new PreventList();
50 | } else {
51 | return null;
52 | }
53 | }
54 | */
55 | public PreventFragment getFragment(int position) {
56 | return mFragments[position];
57 | }
58 |
59 | @Override
60 | public int getCount() {
61 | return 2;
62 | }
63 |
64 |
65 | }
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/SettingsFragment.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.content.SharedPreferences;
4 | import android.os.Bundle;
5 |
6 |
7 | import androidx.preference.PreferenceFragmentCompat;
8 |
9 | import me.piebridge.prevent.R;
10 | import me.piebridge.prevent.ui.util.PreventUtils;
11 |
12 | public class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
13 | @Override
14 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
15 | addPreferencesFromResource(R.xml.settings);
16 | this.getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
17 | }
18 |
19 | @Override
20 | public void onDestroy() {
21 | this.getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
22 | super.onDestroy();
23 | }
24 |
25 | @Override
26 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
27 | PreventUtils.updateConfiguration(getContext());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/SystemApplications.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.content.pm.ApplicationInfo;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 |
7 | import java.util.HashSet;
8 | import java.util.Set;
9 |
10 | import me.piebridge.prevent.common.PackageUtils;
11 |
12 | public class SystemApplications extends PreventFragment {
13 |
14 | @Override
15 | protected Set getPackageNames() {
16 | Set names = new HashSet();
17 | PackageManager pm = getActivity().getPackageManager();
18 | for (PackageInfo pkgInfo : pm.getInstalledPackages(0)) {
19 | ApplicationInfo appInfo = pkgInfo.applicationInfo;
20 | if (PackageUtils.isSystemPackage(appInfo.flags)) {
21 | names.add(appInfo.packageName);
22 | }
23 | }
24 | return names;
25 | }
26 |
27 |
28 |
29 | }
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/UILog.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by thom on 15/7/25.
7 | */
8 | public class UILog {
9 |
10 | public static final String TAG = "PreventUI";
11 |
12 | private UILog() {
13 |
14 | }
15 |
16 | public static void d(String msg) {
17 | Log.d(TAG, msg);
18 | }
19 |
20 | public static void d(String msg, Throwable t) { // NOSONAR
21 | Log.d(TAG, msg);
22 | }
23 |
24 | public static void i(String msg) {
25 | Log.i(TAG, msg);
26 | }
27 |
28 | public static void e(String msg) {
29 | Log.e(TAG, msg);
30 | }
31 |
32 | public static void e(String msg, Throwable t) {
33 | Log.e(TAG, msg, t);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/ColorUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import android.content.Context;
4 | import android.util.TypedValue;
5 |
6 | import androidx.core.content.ContextCompat;
7 |
8 | import java.text.DecimalFormat;
9 | import java.util.Locale;
10 |
11 | /**
12 | * Created by thom on 16/7/4.
13 | */
14 | public class ColorUtils {
15 |
16 | private static DecimalFormat DF = new DecimalFormat("#.##");
17 |
18 | private static final int A = 24;
19 | private static final int R = 16;
20 | private static final int G = 8;
21 | private static final double MAX = 255.0;
22 |
23 | private ColorUtils() {
24 |
25 | }
26 |
27 | public static String rgba(int color) {
28 | // 0xAARRGGBB
29 | int a = (color >>> A) & 0xff;
30 | int r = (color >>> R) & 0xff;
31 | int g = (color >>> G) & 0xff;
32 | int b = color & 0xff;
33 | return String.format(Locale.US, "rgba(%d, %d, %d, %s)", r, g, b, DF.format(a / MAX));
34 | }
35 |
36 | public static String blend(int source, int drop) {
37 | int sourceA = (source >>> A) & 0xff;
38 | int sourceR = (source >>> R) & 0xff;
39 | int sourceG = (source >>> G) & 0xff;
40 | int sourceB = source & 0xff;
41 | if (sourceA == 0) {
42 | sourceA = 0xff;
43 | }
44 |
45 | int dropA = (drop >>> A) & 0xff;
46 | int dropR = (drop >>> R) & 0xff;
47 | int dropG = (drop >>> G) & 0xff;
48 | int dropB = drop & 0xff;
49 | if (dropA == 0) {
50 | dropA = 0xff;
51 | }
52 |
53 | // blend: WebKit/Source/platform/graphics/Color.cpp
54 | int d = 0xff * (sourceA + dropA) - sourceA * dropA;
55 | int targetA = d / 0xff;
56 | int targetR = (sourceR * sourceA * (0xff - dropA) + 0xff * dropA * dropR) / d;
57 | int targetG = (sourceG * sourceA * (0xff - dropA) + 0xff * dropA * dropG) / d;
58 | int targetB = (sourceB * sourceA * (0xff - dropA) + 0xff * dropA * dropB) / d;
59 |
60 | return String.format(Locale.US, "rgba(%d, %d, %d, %s)", targetR, targetG, targetB,
61 | DF.format(targetA / MAX));
62 | }
63 |
64 | public static int replaceAlpha(int source, int alpha) {
65 | return (source & 0xffffff) | (alpha & 0xff000000);
66 | }
67 |
68 | public static int fixOpacity(int color) {
69 | int a = (color >>> A) & 0xff;
70 | if (a == 0xff) {
71 | // use 87% opacity
72 | return replaceAlpha(color, 0xde000000);
73 | } else {
74 | return color;
75 | }
76 | }
77 |
78 | public static int resolveColor(Context context, int resId) {
79 | TypedValue tv = new TypedValue();
80 | context.getTheme().resolveAttribute(resId, tv, true);
81 | if (isColor(tv.type)) {
82 | return tv.data;
83 | } else {
84 | return ContextCompat.getColor(context, tv.resourceId);
85 | }
86 | }
87 |
88 | private static boolean isColor(int type) {
89 | return type >= TypedValue.TYPE_FIRST_COLOR_INT && type <= TypedValue.TYPE_LAST_COLOR_INT;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/EmailUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import android.content.ActivityNotFoundException;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.os.Build;
8 | import android.text.TextUtils;
9 |
10 | import java.io.File;
11 | import java.util.Locale;
12 |
13 | import me.piebridge.prevent.BuildConfig;
14 | import me.piebridge.prevent.R;
15 | import me.piebridge.prevent.ui.UILog;
16 |
17 | /**
18 | * Created by thom on 15/10/5.
19 | */
20 | public class EmailUtils {
21 |
22 | private EmailUtils() {
23 |
24 | }
25 |
26 | public static String getSubject(Context context) {
27 | return context.getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME +
28 | "(Android " + Locale.getDefault().toString() + "-" + Build.VERSION.RELEASE + ")";
29 | }
30 |
31 | public static boolean sendEmail(Context context, String content) {
32 | if (TextUtils.isEmpty(BuildConfig.EMAIL)) {
33 | return false;
34 | }
35 | Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:" + BuildConfig.EMAIL));
36 | intent.putExtra(Intent.EXTRA_SUBJECT, getSubject(context));
37 | if (content != null) {
38 | intent.putExtra(Intent.EXTRA_TEXT, content);
39 | }
40 | try {
41 | context.startActivity(Intent.createChooser(intent, context.getString(R.string.choose_email)));
42 | return true;
43 | } catch (ActivityNotFoundException e) {
44 | UILog.d("cannot send email", e);
45 | return false;
46 | }
47 | }
48 |
49 | public static void sendZip(Context context, File path, String content) {
50 | if (TextUtils.isEmpty(BuildConfig.EMAIL)) {
51 | return;
52 | }
53 | Intent intent = new Intent(Intent.ACTION_SEND);
54 | intent.addCategory(Intent.CATEGORY_DEFAULT);
55 | intent.setType("vnd.android.cursor.dir/email");
56 | intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(path));
57 | intent.putExtra(Intent.EXTRA_SUBJECT, EmailUtils.getSubject(context));
58 | intent.putExtra(Intent.EXTRA_TEXT, content);
59 | intent.putExtra(Intent.EXTRA_EMAIL, new String[]{BuildConfig.EMAIL});
60 | context.startActivity(Intent.createChooser(intent, context.getString(R.string.choose_email)));
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/FileUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import java.io.BufferedInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.io.OutputStream;
11 |
12 | /**
13 | * Created by thom on 15/10/21.
14 | */
15 | public class FileUtils {
16 |
17 | private FileUtils() {
18 |
19 | }
20 |
21 | public static void dumpFile(InputStream is, File file) throws IOException {
22 | byte[] buffer = new byte[0x1000];
23 | OutputStream os = new FileOutputStream(file);
24 | int length;
25 | while ((length = is.read(buffer)) > 0) {
26 | os.write(buffer, 0, length);
27 | }
28 | os.flush();
29 | is.close();
30 | }
31 |
32 | public static void copyFile(OutputStream os, File file) throws IOException {
33 | byte[] buffer = new byte[0x1000];
34 | InputStream is = new FileInputStream(file);
35 | int length;
36 | while ((length = is.read(buffer)) > 0) {
37 | os.write(buffer, 0, length);
38 | }
39 | os.flush();
40 | is.close();
41 | }
42 |
43 | public static String readAsString(InputStream is) throws IOException {
44 | int length;
45 | byte[] bytes = new byte[0x2000];
46 | BufferedInputStream bis = new BufferedInputStream(is);
47 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
48 | while ((length = bis.read(bytes)) != -1) {
49 | bos.write(bytes, 0, length);
50 | }
51 | bis.close();
52 | return bos.toString("UTF-8");
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/LabelLoader.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageManager;
7 |
8 | import java.util.Locale;
9 | import java.util.concurrent.ScheduledThreadPoolExecutor;
10 |
11 | /**
12 | * Created by thom on 16/1/28.
13 | */
14 | public class LabelLoader {
15 |
16 | private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(0x2);
17 |
18 | private final SharedPreferences mSp;
19 |
20 | private final PackageManager mPm;
21 |
22 | public LabelLoader(Context context) {
23 | mSp = context.getSharedPreferences("label-" + Locale.getDefault(), Context.MODE_PRIVATE);
24 | mPm = context.getPackageManager();
25 | }
26 |
27 | public String loadLabel(ApplicationInfo info) {
28 | String packageName = info.packageName;
29 | if (mSp.contains(packageName)) {
30 | loadLabelIfNeeded(info);
31 | return mSp.getString(packageName, packageName);
32 | } else {
33 | String label = StringUtils.trim(info.loadLabel(mPm)).toString();
34 | mSp.edit().putString(packageName, label).apply();
35 | return label;
36 | }
37 | }
38 |
39 | private void loadLabelIfNeeded(final ApplicationInfo info) {
40 | executor.submit(new Runnable() {
41 | @Override
42 | public void run() {
43 | String packageName = info.packageName;
44 | String label = StringUtils.trim(info.loadLabel(mPm)).toString();
45 | if (!label.equals(mSp.getString(packageName, packageName))) {
46 | mSp.edit().putString(packageName, label).apply();
47 | }
48 | }
49 | });
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/PreventListUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.pm.PackageManager;
6 | import android.os.Environment;
7 |
8 | import androidx.preference.PreferenceManager;
9 |
10 | import java.io.File;
11 | import java.util.Set;
12 |
13 | import me.piebridge.prevent.BuildConfig;
14 | import me.piebridge.prevent.common.ExternalFileUtils;
15 | import me.piebridge.prevent.common.FileUtils;
16 | import me.piebridge.prevent.common.PreventIntent;
17 | import me.piebridge.prevent.ui.UILog;
18 |
19 | public final class PreventListUtils {
20 |
21 | private static boolean synced = false;
22 |
23 | private static PreventListUtils preventListUtils = new PreventListUtils();
24 |
25 | private PreventListUtils() {
26 |
27 | }
28 |
29 | protected String getPrevent(Context context) {
30 | String dataDir;
31 | try {
32 | dataDir = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).applicationInfo.dataDir;
33 | } catch (PackageManager.NameNotFoundException e) {
34 | UILog.d("cannot find package for context: " + context, e);
35 | dataDir = Environment.getDataDirectory() + "/data/" + BuildConfig.APPLICATION_ID;
36 | }
37 | return new File(new File(dataDir, "conf"), FileUtils.PREVENT_LIST).getAbsolutePath();
38 | }
39 |
40 | public synchronized void backupIfNeeded(Context context, Set packages) {
41 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
42 | boolean backup = false;
43 | try {
44 | backup = sp.getBoolean(PreventIntent.KEY_BACKUP_PREVENT_LIST, false);
45 | } catch (ClassCastException e) {
46 | UILog.d("invalid value for " + PreventIntent.KEY_BACKUP_PREVENT_LIST, e);
47 | sp.edit().putBoolean(PreventIntent.KEY_BACKUP_PREVENT_LIST, false).apply();
48 | }
49 | for (File dir : ExternalFileUtils.getExternalFilesDirs(context)) {
50 | if (dir == null) {
51 | continue;
52 | }
53 | File file = new File(dir, FileUtils.PREVENT_LIST);
54 | if (backup) {
55 | FileUtils.save(file.getAbsolutePath(), packages);
56 | } else if (file.exists()) {
57 | FileUtils.eraseFiles(file);
58 | }
59 | }
60 | File file = new File(getPrevent(context));
61 | if (file.exists()) {
62 | FileUtils.eraseFiles(file);
63 | }
64 | }
65 |
66 | public boolean syncIfNeeded(Context context, Set packages) {
67 | boolean updated = false;
68 | if (packages.isEmpty() && !synced) {
69 | synced = true;
70 | updated = PreventUtils.updateConfiguration(context, true);
71 | }
72 | backupIfNeeded(context, packages);
73 | return updated;
74 | }
75 |
76 | public Set load(Context context) {
77 | return FileUtils.load(context, getPrevent(context));
78 | }
79 |
80 | public static PreventListUtils getInstance() {
81 | return preventListUtils;
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/ReportUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.os.Environment;
6 |
7 | import java.io.File;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.util.zip.ZipEntry;
11 | import java.util.zip.ZipOutputStream;
12 |
13 | import me.piebridge.prevent.BuildConfig;
14 | import me.piebridge.prevent.common.PreventIntent;
15 | import me.piebridge.prevent.ui.UILog;
16 |
17 | /**
18 | * Created by thom on 15/12/14.
19 | */
20 | public class ReportUtils {
21 |
22 | private ReportUtils() {
23 |
24 | }
25 |
26 | public static void reportBug(Context context) {
27 | File dir = context.getExternalFilesDir(null);
28 | File cacheDir = context.getExternalCacheDir();
29 | if (dir == null || cacheDir == null) {
30 | return;
31 | }
32 | try {
33 | File path = new File(dir, "logs-v" + BuildConfig.VERSION_NAME + ".zip");
34 | final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(path));
35 |
36 | File[] caches = cacheDir.listFiles();
37 | if (caches == null) {
38 | caches = new File[0];
39 | }
40 | boolean empty = true;
41 | for (File file : caches) {
42 | empty = false;
43 | zos.putNextEntry(new ZipEntry(file.getName()));
44 | FileUtils.copyFile(zos, file);
45 | zos.closeEntry();
46 | }
47 |
48 | File xposedLog = new File(Environment.getDataDirectory() + "/data/de.robv.android.xposed.installer/log/error.log");
49 | if (xposedLog.isFile() && xposedLog.canRead()) {
50 | empty = false;
51 | zos.putNextEntry(new ZipEntry("xposed.log"));
52 | FileUtils.copyFile(zos, xposedLog);
53 | zos.closeEntry();
54 | }
55 |
56 | if (empty) {
57 | zos.putNextEntry(new ZipEntry("empty"));
58 | zos.closeEntry();
59 | }
60 |
61 | zos.close();
62 | EmailUtils.sendZip(context, path, Build.FINGERPRINT);
63 | } catch (IOException e) {
64 | UILog.e("cannot report bug", e);
65 | }
66 | }
67 |
68 | public static void clearReport(Context context) {
69 | File dir = context.getExternalCacheDir();
70 | if (dir == null) {
71 | return;
72 | }
73 | for (File file : dir.listFiles()) {
74 | String path = file.getName();
75 | if (!path.startsWith(PreventIntent.LOGCAT_BOOT)) {
76 | file.delete();
77 | }
78 | }
79 | }
80 |
81 | public static void waitForCompleted(Context context) {
82 | File dir = context.getExternalCacheDir();
83 | if (dir == null) {
84 | return;
85 | }
86 | while (true) {
87 | for (File file : dir.listFiles()) {
88 | String path = file.getName();
89 | if (PreventIntent.LOGCAT_COMPLETED.equals(path)) {
90 | return;
91 | }
92 | }
93 | try {
94 | Thread.sleep(0x400);
95 | } catch (InterruptedException e) {
96 | UILog.d("cannot sleep", e);
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/StatusUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 | import android.text.format.DateUtils;
6 | import android.util.SparseIntArray;
7 |
8 | import java.util.Iterator;
9 | import java.util.LinkedHashSet;
10 | import java.util.Set;
11 |
12 | import me.piebridge.prevent.R;
13 | import me.piebridge.prevent.common.TimeUtils;
14 |
15 | /**
16 | * Created by thom on 15/10/17.
17 | */
18 | public class StatusUtils {
19 |
20 | private static SparseIntArray statusMap = new SparseIntArray();
21 |
22 | static {
23 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND, R.string.importance_background);
24 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY, R.string.importance_empty);
25 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND, R.string.importance_foreground);
26 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, R.string.importance_foreground_service);
27 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, R.string.importance_gone);
28 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE, R.string.importance_perceptible);
29 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, R.string.importance_service);
30 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING, R.string.importance_top_sleeping);
31 | statusMap.put(ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE, R.string.importance_visible);
32 | statusMap.put(-ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND, R.string.importance_inactive);
33 | statusMap.put(-ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, R.string.importance_service_not_started);
34 | statusMap.put(9003, R.string.extra_launcher);
35 | statusMap.put(9004, R.string.extra_input_method);
36 | statusMap.put(9001, R.string.extra_audio_focused);
37 | statusMap.put(9002, R.string.extra_vpn_established);
38 | }
39 |
40 | private StatusUtils() {
41 |
42 | }
43 |
44 | public static CharSequence formatRunning(Context context, Set running) {
45 | if (running == null) {
46 | return context.getString(R.string.not_running);
47 | } else {
48 | if (running.contains((long) ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) && running.contains((long) -ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE)) {
49 | running.remove((long) -ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE);
50 | }
51 | return doFormatRunning(context, running);
52 | }
53 | }
54 |
55 | private static CharSequence doFormatRunning(Context context, Set running) {
56 | Set sets = new LinkedHashSet();
57 | long now = TimeUtils.now();
58 | for (Long i : running) {
59 | int v = statusMap.get(i.intValue());
60 | if (v == 0) {
61 | long elapsed = now - Math.abs(i);
62 | sets.add(DateUtils.formatElapsedTime(elapsed));
63 | } else {
64 | sets.add(context.getString(v));
65 | }
66 | }
67 | return toString(sets);
68 | }
69 |
70 | private static CharSequence toString(Set sets) {
71 | StringBuilder buffer = new StringBuilder();
72 | Iterator> it = sets.iterator();
73 | while (it.hasNext()) {
74 | buffer.append(it.next());
75 | if (it.hasNext()) {
76 | buffer.append(", ");
77 | } else {
78 | break;
79 | }
80 | }
81 | return buffer.toString();
82 | }
83 |
84 |
85 | private static boolean isPriority(Set running) {
86 | for (Long i : running) {
87 | int v = statusMap.get(i.intValue());
88 | if (v == 0) {
89 | return i < 0;
90 | }
91 | }
92 | return false;
93 | }
94 |
95 | public static int getDrawable(Set running, boolean prevent) {
96 | if (running == null) {
97 | return R.drawable.ic_menu_block;
98 | }
99 | if (isPriority(running)) {
100 | return R.drawable.ic_menu_star;
101 | } else if (prevent) {
102 | return R.drawable.ic_menu_block;
103 | } else {
104 | return R.drawable.ic_menu_stop;
105 | }
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/StringUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | /**
4 | * Created by thom on 16/1/28.
5 | */
6 | public class StringUtils {
7 |
8 | private static final char[] WHITESPACE = ("\u0085\u00a0\u1680"
9 | + "\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a"
10 | + "\u2028\u2029\u202f\u205f\u3000").toCharArray();
11 |
12 | private StringUtils() {
13 |
14 | }
15 |
16 | private static boolean isWhiteSpace(char s) {
17 | if (s <= ' ') {
18 | return true;
19 | }
20 | for (char c : WHITESPACE) {
21 | if (c == s) {
22 | return true;
23 | }
24 | }
25 | return false;
26 | }
27 |
28 | public static CharSequence trim(CharSequence cs) {
29 | if (cs != null) {
30 | int last = cs.length() - 1;
31 | int start = 0;
32 | int end = last;
33 | while (start <= end && isWhiteSpace(cs.charAt(start))) {
34 | ++start;
35 | }
36 | while (end >= start && isWhiteSpace(cs.charAt(end))) {
37 | --end;
38 | }
39 | if (start != 0 || end != last) {
40 | return cs.subSequence(start, end + 1);
41 | }
42 | }
43 | return cs;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/me/piebridge/prevent/ui/util/XposedUtils.java:
--------------------------------------------------------------------------------
1 | package me.piebridge.prevent.ui.util;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.Map;
5 |
6 | /**
7 | * Created by thom on 15/10/13.
8 | */
9 | public class XposedUtils {
10 |
11 | private XposedUtils() {
12 |
13 | }
14 |
15 | public static void disableXposed(Class> clazz) {
16 | try {
17 | Field field = clazz.getDeclaredField("sHookedMethodCallbacks");
18 | field.setAccessible(true);
19 | Map sHookedMethodCallbacks = (Map) field.get(null);
20 | Object doNothing = Class.forName("de.robv.android.xposed.XC_MethodReplacement", false, clazz.getClassLoader()).getField("DO_NOTHING").get(null);
21 | for (Object callbacks : sHookedMethodCallbacks.values()) {
22 | field = callbacks.getClass().getDeclaredField("elements");
23 | field.setAccessible(true);
24 | Object[] elements = (Object[]) field.get(callbacks);
25 | for (int i = 0; i < elements.length; ++i) {
26 | elements[i] = doNothing;
27 | }
28 | }
29 | } catch (Throwable t) { // NOSONAR
30 | // do nothing
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | maven {
7 | url 'https://dl.google.com/dl/android/maven2'
8 | }
9 |
10 | }
11 | dependencies {
12 | classpath "com.android.tools.build:gradle:3.0.0"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 | ext{
19 | version = [
20 | MIN_SDK_VERSION : 29,
21 | COMPILE_SDK_VERSION : 30,
22 | TARGET_SDK_VERSION : 30,
23 | ]
24 | }
25 |
26 | allprojects {
27 | repositories {
28 | google()
29 | jcenter()
30 | mavenCentral()
31 | maven {
32 | url 'https://dl.google.com/dl/android/maven2'
33 | }
34 |
35 | }
36 | }
37 |
38 | task clean(type: Delete) {
39 | delete rootProject.buildDir
40 | }
--------------------------------------------------------------------------------
/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=-Xmx2048m -Dfile.encoding=UTF-8
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 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Sep 30 20:53: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-4.4-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/groupedadapter/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 |
4 |
5 | def VERSION_NAME = "1.2"
6 |
7 | android {
8 | compileSdkVersion rootProject.ext.version.COMPILE_SDK_VERSION
9 | buildToolsVersion '28.0.3'
10 | defaultConfig {
11 | minSdkVersion rootProject.ext.version.MIN_SDK_VERSION
12 | targetSdkVersion rootProject.ext.version.TARGET_SDK_VERSION
13 | versionCode 1
14 | versionName version
15 |
16 |
17 |
18 | }
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 | implementation 'androidx.databinding:databinding-common:4.1.1'
31 | implementation 'androidx.databinding:databinding-runtime:4.1.1'
32 | compileOnly 'androidx.recyclerview:recyclerview:1.0.0'
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/groupedadapter/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\Administrator\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/groupedadapter/src/androidTest/java/com/donkingliang/groupedadapter/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/groupedadapter/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/groupedadapter/src/main/java/com/donkingliang/groupedadapter/decoration/GroupedGridItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter.decoration;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | import com.donkingliang.groupedadapter.adapter.GroupedRecyclerViewAdapter;
6 |
7 | /**
8 | * @Author donkingliang QQ:1043214265 github:https://github.com/donkingliang
9 | * @Description
10 | * @Date 2020/7/7
11 | */
12 | public class GroupedGridItemDecoration extends AbsGroupedGridItemDecoration {
13 |
14 | private int mHeaderDividerSize;
15 |
16 | private Drawable mHeaderDivider;
17 |
18 | private int mFooterDividerSize;
19 |
20 | private Drawable mFooterDivider;
21 |
22 | private int mChildRowDividerSize;
23 |
24 | private Drawable mChildRowDivider;
25 |
26 | private int mChildColumnDividerSize;
27 |
28 | private Drawable mChildColumnDivider;
29 |
30 | public GroupedGridItemDecoration(GroupedRecyclerViewAdapter adapter,
31 | int headerDividerSize, Drawable headerDivider,
32 | int footerDividerSize, Drawable footerDivider,
33 | int childRowDividerSize, Drawable childRowDivider,
34 | int childColumnDividerSize, Drawable childColumnDivider) {
35 | super(adapter);
36 | this.mHeaderDividerSize = headerDividerSize;
37 | this.mHeaderDivider = headerDivider;
38 | this.mFooterDividerSize = footerDividerSize;
39 | this.mFooterDivider = footerDivider;
40 | this.mChildRowDividerSize = childRowDividerSize;
41 | this.mChildRowDivider = childRowDivider;
42 | this.mChildColumnDividerSize = childColumnDividerSize;
43 | this.mChildColumnDivider = childColumnDivider;
44 | }
45 |
46 | @Override
47 | public int getHeaderDividerSize(int groupPosition) {
48 | return mHeaderDividerSize;
49 | }
50 |
51 | @Override
52 | public Drawable getHeaderDivider(int groupPosition) {
53 | return mHeaderDivider;
54 | }
55 |
56 | @Override
57 | public int getFooterDividerSize(int groupPosition) {
58 | return mFooterDividerSize;
59 | }
60 |
61 | @Override
62 | public Drawable getFooterDivider(int groupPosition) {
63 | return mFooterDivider;
64 | }
65 |
66 | @Override
67 | public int getChildColumnDividerSize(int groupPosition, int childPosition) {
68 | return mChildColumnDividerSize;
69 | }
70 |
71 | @Override
72 | public Drawable getChildColumnDivider(int groupPosition, int childPosition) {
73 | return mChildColumnDivider;
74 | }
75 |
76 | @Override
77 | public int getChildRowDividerSize(int groupPosition, int childPosition) {
78 | return mChildRowDividerSize;
79 | }
80 |
81 | @Override
82 | public Drawable getChildRowDivider(int groupPosition, int childPosition) {
83 | return mChildRowDivider;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/groupedadapter/src/main/java/com/donkingliang/groupedadapter/decoration/GroupedLinearItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter.decoration;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | import com.donkingliang.groupedadapter.adapter.GroupedRecyclerViewAdapter;
6 |
7 | /**
8 | * @Author donkingliang QQ:1043214265 github:https://github.com/donkingliang
9 | * @Description
10 | * @Date 2020/7/6
11 | */
12 | public class GroupedLinearItemDecoration extends AbsGroupedLinearItemDecoration {
13 |
14 | private int mHeaderDividerSize;
15 |
16 | private Drawable mHeaderDivider;
17 |
18 | private int mFooterDividerSize;
19 |
20 | private Drawable mFooterDivider;
21 |
22 | private int mChildDividerSize;
23 |
24 | private Drawable mChildDivider;
25 |
26 | public GroupedLinearItemDecoration(GroupedRecyclerViewAdapter adapter,
27 | int headerDividerSize, Drawable headerDivider,
28 | int footerDividerSize, Drawable footerDivider,
29 | int childDividerSize, Drawable childDivider) {
30 | super(adapter);
31 | this.mHeaderDividerSize = headerDividerSize;
32 | this.mHeaderDivider = headerDivider;
33 | this.mFooterDividerSize = footerDividerSize;
34 | this.mFooterDivider = footerDivider;
35 | this.mChildDividerSize = childDividerSize;
36 | this.mChildDivider = childDivider;
37 | }
38 |
39 | @Override
40 | public int getChildDividerSize(int groupPosition, int ChildPosition) {
41 | return mChildDividerSize;
42 | }
43 |
44 | @Override
45 | public Drawable getChildDivider(int groupPosition, int ChildPosition) {
46 | return mChildDivider;
47 | }
48 |
49 | @Override
50 | public int getHeaderDividerSize(int groupPosition) {
51 | return mHeaderDividerSize;
52 | }
53 |
54 | @Override
55 | public Drawable getHeaderDivider(int groupPosition) {
56 | return mHeaderDivider;
57 | }
58 |
59 | @Override
60 | public int getFooterDividerSize(int groupPosition) {
61 | return mFooterDividerSize;
62 | }
63 |
64 | @Override
65 | public Drawable getFooterDivider(int groupPosition) {
66 | return mFooterDivider;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/groupedadapter/src/main/java/com/donkingliang/groupedadapter/decoration/IGroupedItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter.decoration;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | import androidx.recyclerview.widget.RecyclerView;
6 |
7 | /**
8 | * @Author donkingliang QQ:1043214265 github:https://github.com/donkingliang
9 | * @Description
10 | * @Date 2020/7/6
11 | */
12 | public interface IGroupedItemDecoration {
13 |
14 | int getHeaderDividerSize(int groupPosition);
15 |
16 | Drawable getHeaderDivider(int groupPosition);
17 |
18 | int getFooterDividerSize(int groupPosition);
19 |
20 | Drawable getFooterDivider(int groupPosition);
21 |
22 | int getChildColumnDividerSize(int groupPosition, int childPosition);
23 |
24 | Drawable getChildColumnDivider(int groupPosition, int childPosition);
25 |
26 | int getChildRowDividerSize(int groupPosition, int childPosition);
27 |
28 | Drawable getChildRowDivider(int groupPosition, int childPosition);
29 |
30 | /**
31 | * 检测是否是可支持的LayoutManager
32 | *
33 | * @param view
34 | * @return
35 | */
36 | boolean checkLayoutManager(RecyclerView view);
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/groupedadapter/src/main/java/com/donkingliang/groupedadapter/holder/BaseViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter.holder;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.drawable.Drawable;
5 | import android.util.SparseArray;
6 | import android.view.View;
7 | import android.widget.ImageView;
8 | import android.widget.TextView;
9 |
10 | import androidx.databinding.DataBindingUtil;
11 | import androidx.databinding.ViewDataBinding;
12 | import androidx.recyclerview.widget.RecyclerView;
13 |
14 | /**
15 | * 通用的RecyclerView.ViewHolder。提供了根据viewId获取View的方法。
16 | * 提供了对View、TextView、ImageView的常用设置方法。
17 | */
18 | public class BaseViewHolder extends RecyclerView.ViewHolder {
19 |
20 | private SparseArray mViews;
21 |
22 | public BaseViewHolder(View itemView) {
23 | super(itemView);
24 | mViews = new SparseArray<>();
25 | }
26 |
27 | /**
28 | * 获取item对应的ViewDataBinding对象
29 | *
30 | * @param
31 | * @return
32 | */
33 | public T getBinding() {
34 | return DataBindingUtil.getBinding(this.itemView);
35 | }
36 |
37 | /**
38 | * 根据View Id 获取对应的View
39 | *
40 | * @param viewId
41 | * @param
42 | * @return
43 | */
44 | public T get(int viewId) {
45 | View view = mViews.get(viewId);
46 | if (view == null) {
47 | view = this.itemView.findViewById(viewId);
48 | mViews.put(viewId, view);
49 | }
50 | return (T) view;
51 | }
52 |
53 | //******** 提供对View、TextView、ImageView的常用设置方法 ******//
54 |
55 | public BaseViewHolder setText(int viewId, CharSequence text) {
56 | TextView tv = get(viewId);
57 | tv.setText(text);
58 | return this;
59 | }
60 |
61 | public BaseViewHolder setText(int viewId, int textRes) {
62 | TextView tv = get(viewId);
63 | tv.setText(textRes);
64 | return this;
65 | }
66 |
67 | public BaseViewHolder setTextColor(int viewId, int textColor) {
68 | TextView view = get(viewId);
69 | view.setTextColor(textColor);
70 | return this;
71 | }
72 |
73 | public BaseViewHolder setTextSize(int viewId, float size) {
74 | TextView view = get(viewId);
75 | view.setTextSize(size);
76 | return this;
77 | }
78 |
79 | public BaseViewHolder setImageResource(int viewId, int resId) {
80 | ImageView view = get(viewId);
81 | view.setImageResource(resId);
82 | return this;
83 | }
84 |
85 | public BaseViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
86 | ImageView view = get(viewId);
87 | view.setImageBitmap(bitmap);
88 | return this;
89 | }
90 |
91 |
92 | public BaseViewHolder setImageDrawable(int viewId, Drawable drawable) {
93 | ImageView view = get(viewId);
94 | view.setImageDrawable(drawable);
95 | return this;
96 | }
97 |
98 |
99 | public BaseViewHolder setBackgroundColor(int viewId, int color) {
100 | View view = get(viewId);
101 | view.setBackgroundColor(color);
102 | return this;
103 | }
104 |
105 | public BaseViewHolder setBackgroundRes(int viewId, int backgroundRes) {
106 | View view = get(viewId);
107 | view.setBackgroundResource(backgroundRes);
108 | return this;
109 | }
110 |
111 | public BaseViewHolder setVisible(int viewId, boolean visible) {
112 | View view = get(viewId);
113 | view.setVisibility(visible ? View.VISIBLE : View.GONE);
114 | return this;
115 | }
116 |
117 | public BaseViewHolder setVisible(int viewId, int visible) {
118 | View view = get(viewId);
119 | view.setVisibility(visible);
120 | return this;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/groupedadapter/src/main/java/com/donkingliang/groupedadapter/layoutmanger/GroupedGridLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter.layoutmanger;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 |
6 | import com.donkingliang.groupedadapter.adapter.GroupedRecyclerViewAdapter;
7 |
8 | import androidx.recyclerview.widget.GridLayoutManager;
9 |
10 |
11 | /**
12 | * 为分组列表提供的GridLayoutManager。
13 | * 因为分组列表如果要使用GridLayoutManager实现网格布局。要保证组的头部和尾部是要单独占用一行的。
14 | * 否则组的头、尾可能会跟子项混着一起,造成布局混乱。
15 | */
16 | public class GroupedGridLayoutManager extends GridLayoutManager {
17 |
18 | private GroupedRecyclerViewAdapter mAdapter;
19 |
20 | public GroupedGridLayoutManager(Context context, int spanCount,
21 | GroupedRecyclerViewAdapter adapter) {
22 | super(context, spanCount);
23 | mAdapter = adapter;
24 | setSpanSizeLookup();
25 | }
26 |
27 | public GroupedGridLayoutManager(Context context, int spanCount, int orientation,
28 | boolean reverseLayout, GroupedRecyclerViewAdapter adapter) {
29 | super(context, spanCount, orientation, reverseLayout);
30 | this.mAdapter = adapter;
31 | setSpanSizeLookup();
32 | }
33 |
34 | public GroupedGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
35 | int defStyleRes, GroupedRecyclerViewAdapter adapter) {
36 | super(context, attrs, defStyleAttr, defStyleRes);
37 | this.mAdapter = adapter;
38 | setSpanSizeLookup();
39 | }
40 |
41 | private void setSpanSizeLookup() {
42 | super.setSpanSizeLookup(new SpanSizeLookup() {
43 | @Override
44 | public int getSpanSize(int position) {
45 | int count = getSpanCount();
46 | if (mAdapter != null) {
47 | int type = mAdapter.judgeType(position);
48 | //只对子项做Grid效果
49 | if (type == GroupedRecyclerViewAdapter.TYPE_CHILD) {
50 | int groupPosition = mAdapter.getGroupPositionForPosition(position);
51 | int childPosition =
52 | mAdapter.getChildPositionForPosition(groupPosition, position);
53 | return getChildSpanSize(groupPosition, childPosition);
54 | }
55 | }
56 |
57 | return count;
58 | }
59 | });
60 | }
61 |
62 | /**
63 | * 提供这个方法可以使外部改变子项的SpanSize。
64 | * 这个方法的作用跟{@link SpanSizeLookup#getSpanSize(int)}一样。
65 | * @param groupPosition
66 | * @param childPosition
67 | * @return
68 | */
69 | public int getChildSpanSize(int groupPosition, int childPosition) {
70 | return 1;
71 | }
72 |
73 | @Override
74 | public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
75 |
76 | }
77 | }
--------------------------------------------------------------------------------
/groupedadapter/src/main/java/com/donkingliang/groupedadapter/structure/GroupStructure.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter.structure;
2 |
3 | /**
4 | * 这个类是用来记录分组列表中组的结构的。
5 | * 通过GroupStructure记录每个组是否有头部,是否有尾部和子项的数量。从而能方便的计算
6 | * 列表的长度和每个组的组头、组尾和子项在列表中的位置。
7 | */
8 | public class GroupStructure {
9 |
10 | private boolean hasHeader;
11 | private boolean hasFooter;
12 | private int childrenCount;
13 |
14 | public GroupStructure(boolean hasHeader, boolean hasFooter, int childrenCount) {
15 | this.hasHeader = hasHeader;
16 | this.hasFooter = hasFooter;
17 | this.childrenCount = childrenCount;
18 | }
19 |
20 | public boolean hasHeader() {
21 | return hasHeader;
22 | }
23 |
24 | public void setHasHeader(boolean hasHeader) {
25 | this.hasHeader = hasHeader;
26 | }
27 |
28 | public boolean hasFooter() {
29 | return hasFooter;
30 | }
31 |
32 | public void setHasFooter(boolean hasFooter) {
33 | this.hasFooter = hasFooter;
34 | }
35 |
36 | public int getChildrenCount() {
37 | return childrenCount;
38 | }
39 |
40 | public void setChildrenCount(int childrenCount) {
41 | this.childrenCount = childrenCount;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/groupedadapter/src/main/res/drawable-xhdpi/group_adapter_empty_view_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saint-Theana/Prevent-App/052d62d7736e313d736d341b1cbf1d9285345260/groupedadapter/src/main/res/drawable-xhdpi/group_adapter_empty_view_image.png
--------------------------------------------------------------------------------
/groupedadapter/src/main/res/layout/group_adapter_default_empty_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/groupedadapter/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/groupedadapter/src/test/java/com/donkingliang/groupedadapter/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.donkingliang.groupedadapter;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app',":groupedadapter"
2 | rootProject.name = "blackzone-new"
--------------------------------------------------------------------------------