├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── AndroidHiddenAPI │ ├── android │ │ ├── app │ │ │ ├── ActionBar.java │ │ │ ├── ActivityManager.java │ │ │ ├── ActivityThread.java │ │ │ ├── AppGlobals.java │ │ │ ├── AppOpsManager.java │ │ │ ├── ApplicationThread.java │ │ │ ├── IApplicationThread.java │ │ │ ├── INotificationManager.java │ │ │ ├── IServiceConnection.java │ │ │ ├── ProfilerInfo.java │ │ │ └── usage │ │ │ │ └── IUsageStatsManager.java │ │ ├── content │ │ │ ├── IIntentReceiver.java │ │ │ └── pm │ │ │ │ ├── IPackageManager.java │ │ │ │ └── PackageParser.java │ │ ├── os │ │ │ ├── Process.java │ │ │ ├── ServiceManager.java │ │ │ └── SystemProperties.java │ │ └── view │ │ │ └── IApplicationToken.java │ └── com │ │ └── android │ │ ├── internal │ │ └── app │ │ │ └── IAppOpsService.java │ │ └── server │ │ └── am │ │ ├── ActivityRecord.java │ │ ├── ProcessRecord.java │ │ └── TaskRecord.java ├── AndroidManifest.xml ├── DEVELOP.md ├── LICENSE ├── Makefile ├── README.md ├── assets │ ├── about.en.html │ ├── about.zh.html │ ├── patch.py │ └── wechat.png ├── build.gradle ├── lib │ └── hidenapi.jar ├── res │ ├── drawable-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_menu_block.png │ │ ├── ic_menu_prevent.png │ │ ├── ic_menu_recover.png │ │ ├── ic_menu_star.png │ │ ├── ic_menu_stop.png │ │ └── paypal.png │ ├── drawable-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_menu_block.png │ │ ├── ic_menu_prevent.png │ │ ├── ic_menu_recover.png │ │ ├── ic_menu_star.png │ │ ├── ic_menu_stop.png │ │ ├── icon_right.png │ │ └── paypal.png │ ├── drawable │ │ ├── about.xml │ │ ├── contact.xml │ │ ├── holo_red_dark_ripple_bg.xml │ │ ├── settings.xml │ │ ├── system_app.xml │ │ ├── user_app.xml │ │ └── violet2.jpg │ ├── layout │ │ ├── about.xml │ │ ├── activity_settings.xml │ │ ├── adapter_expandable_header.xml │ │ ├── donate.xml │ │ ├── item.xml │ │ ├── list.xml │ │ ├── main.xml │ │ ├── nav_header.xml │ │ ├── progress_dialog.xml │ │ └── progress_dialog_horizonal.xml │ ├── menu │ │ ├── bottom_nv.xml │ │ └── drawer_nv.xml │ ├── values-de │ │ └── strings.xml │ ├── values-in │ │ └── strings.xml │ ├── values-zh-rCN │ │ └── strings.xml │ ├── values-zh-rTW │ │ └── strings.xml │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── donottranslate.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── xml │ │ └── settings.xml ├── services │ └── core │ │ └── java │ │ └── com │ │ └── android │ │ └── server │ │ └── am │ │ ├── ActivityManagerService.java │ │ ├── ActivityStackSupervisor.java │ │ ├── PreventRunning.java │ │ ├── PreventRunningHook.java │ │ └── PreventRunningUtils.java └── src │ └── me │ └── piebridge │ └── prevent │ ├── common │ ├── CommonLog.java │ ├── Configuration.java │ ├── ExternalFileUtils.java │ ├── FileUtils.java │ ├── PackageUtils.java │ ├── PreventIntent.java │ └── TimeUtils.java │ ├── framework │ ├── ActivityManagerServiceHook.java │ ├── ActivityReceiver.java │ ├── CheckingRunningService.java │ ├── IntentFilterHook.java │ ├── IntentFilterMatchResult.java │ ├── PreventLog.java │ ├── PreventRunning.java │ ├── SystemHook.java │ ├── SystemReceiver.java │ └── util │ │ ├── AccountUtils.java │ │ ├── ActivityManagerServiceUtils.java │ │ ├── ActivityRecordUtils.java │ │ ├── AlarmManagerServiceUtils.java │ │ ├── BroadcastFilterUtils.java │ │ ├── HideApiUtils.java │ │ ├── HookUtils.java │ │ ├── LogUtils.java │ │ ├── LogcatUtils.java │ │ ├── NotificationManagerServiceUtils.java │ │ ├── PreventListUtils.java │ │ ├── ProcessRecordUtils.java │ │ ├── ReflectUtils.java │ │ ├── ResourcesUtils.java │ │ ├── SafeActionUtils.java │ │ └── TaskRecordUtils.java │ └── ui │ ├── AdvancedSettingsActivity.java │ ├── Applications.java │ ├── ListPreferenceSummary.java │ ├── PreventActivity.java │ ├── PreventFragment.java │ ├── PreventProvider.java │ ├── ProgressAlertDialog.java │ ├── ScreenSlidePagerAdapter.java │ ├── SettingsFragment.java │ ├── SystemApplications.java │ ├── UILog.java │ └── util │ ├── ColorUtils.java │ ├── EmailUtils.java │ ├── FileUtils.java │ ├── LabelLoader.java │ ├── PreventListUtils.java │ ├── PreventUtils.java │ ├── ReportUtils.java │ ├── StatusUtils.java │ ├── StringUtils.java │ └── XposedUtils.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── groupedadapter ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── donkingliang │ │ └── groupedadapter │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── donkingliang │ │ │ └── groupedadapter │ │ │ ├── adapter │ │ │ └── GroupedRecyclerViewAdapter.java │ │ │ ├── decoration │ │ │ ├── AbsGroupedGridItemDecoration.java │ │ │ ├── AbsGroupedLinearItemDecoration.java │ │ │ ├── GroupedGridItemDecoration.java │ │ │ ├── GroupedLinearItemDecoration.java │ │ │ └── IGroupedItemDecoration.java │ │ │ ├── holder │ │ │ └── BaseViewHolder.java │ │ │ ├── layoutmanger │ │ │ └── GroupedGridLayoutManager.java │ │ │ ├── structure │ │ │ └── GroupStructure.java │ │ │ └── widget │ │ │ └── StickyHeaderLayout.java │ └── res │ │ ├── drawable-xhdpi │ │ └── group_adapter_empty_view_image.png │ │ ├── layout │ │ └── group_adapter_default_empty_view.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── donkingliang │ └── groupedadapter │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #黑域补丁版 安卓10/11 其他安卓版本不被支持
2 | #本仓库开源App源码以及提供编译好的apk文件,以release形式发布magisk模块。替换模块里的services.jar后刷入即可
3 | #打补丁请移步:
4 | #https://github.com/Saint-Theana/Prevent-Q
5 | #https://github.com/Saint-Theana/Prevent-R
6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /gen 3 | /obj 4 | 5 | ant.properties 6 | local.properties 7 | project.properties 8 | build.xml 9 | custom_rules.xml 10 | proguard-project.txt 11 | 12 | /.classpath 13 | /.project 14 | /.settings 15 | 16 | *.log 17 | 18 | .DS_Store 19 | 20 | /build 21 | /*.iml 22 | /.idea 23 | /.gradle 24 | 25 | *.class 26 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/ActionBar.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | public class ActionBar { 4 | 5 | /* for meizu only */ 6 | public void setActionBarViewCollapsable(boolean collapsable) { 7 | throw new UnsupportedOperationException(); 8 | } 9 | 10 | public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { 11 | throw new UnsupportedOperationException(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/ActivityManager.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | import android.content.ComponentName; 4 | 5 | import java.util.List; 6 | 7 | public class ActivityManager { 8 | 9 | /** 10 | * @hide 11 | */ 12 | public void forceStopPackage(String packageName) { 13 | throw new UnsupportedOperationException(); 14 | } 15 | 16 | public List getRunningServices(int maxNum) throws SecurityException { 17 | throw new UnsupportedOperationException(); 18 | } 19 | 20 | public List getRunningAppProcesses() { 21 | throw new UnsupportedOperationException(); 22 | } 23 | 24 | public static class RunningAppProcessInfo { 25 | public static final int IMPORTANCE_BACKGROUND = 400; 26 | public static final int IMPORTANCE_EMPTY = 500; 27 | public static final int IMPORTANCE_FOREGROUND = 100; 28 | public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; 29 | public static final int IMPORTANCE_GONE = 1000; 30 | public static final int IMPORTANCE_PERCEPTIBLE = 130; 31 | public static final int IMPORTANCE_SERVICE = 300; 32 | public static final int IMPORTANCE_TOP_SLEEPING = 150; 33 | public static final int IMPORTANCE_VISIBLE = 200; 34 | public int importance; 35 | public int pid; 36 | public String[] pkgList = null; 37 | public int uid; 38 | } 39 | 40 | public static class RunningServiceInfo { 41 | public int clientCount; 42 | public int clientLabel; 43 | public int flags; 44 | public int pid; 45 | public String process; 46 | public ComponentName service; 47 | public boolean started; 48 | public int uid; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/ActivityThread.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | /** 4 | * @hide 5 | */ 6 | public class ActivityThread { 7 | 8 | public static Application currentApplication() { 9 | throw new UnsupportedOperationException(); 10 | } 11 | 12 | public ApplicationThread getApplicationThread() { 13 | throw new UnsupportedOperationException(); 14 | } 15 | 16 | private abstract class ApplicationThread implements IApplicationThread { 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/AppGlobals.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | import android.content.pm.IPackageManager; 4 | 5 | public class AppGlobals { 6 | 7 | public static IPackageManager getPackageManager() { 8 | throw new UnsupportedOperationException(); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/AppOpsManager.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | /** 4 | * Created by thom on 2017/12/14. 5 | */ 6 | 7 | public class AppOpsManager { 8 | 9 | public static final int MODE_ALLOWED = 0; 10 | 11 | public static final int MODE_IGNORED = 1; 12 | 13 | /** @hide Control whether an application is allowed to run in the background. */ 14 | public static int OP_RUN_IN_BACKGROUND = 63; 15 | } 16 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/ApplicationThread.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | public class ApplicationThread { 4 | } 5 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/IApplicationThread.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.IBinder; 5 | import android.os.RemoteException; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @hide 11 | */ 12 | public interface IApplicationThread { 13 | 14 | /* for api-10 only */ 15 | void scheduleRelaunchActivity(IBinder token, List pendingResults, List pendingNewIntents, int configChanges, boolean notResumed, Configuration config) throws RemoteException; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/INotificationManager.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | public interface INotificationManager { 4 | 5 | /* - api-24 */ 6 | int getPackagePriority(String pkg, int uid) throws android.os.RemoteException; 7 | 8 | /* api-24 */ 9 | int getPriority(String pkg, int uid) throws android.os.RemoteException; 10 | boolean areNotificationsEnabledForPackage(String var1, int var2) throws android.os.RemoteException; 11 | 12 | class Stub { 13 | 14 | public static android.app.INotificationManager asInterface(android.os.IBinder obj) { 15 | throw new UnsupportedOperationException(); 16 | } 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/IServiceConnection.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | /** 4 | * Created by thom on 2016/10/23. 5 | */ 6 | public class IServiceConnection { 7 | } 8 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/ProfilerInfo.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | /** 4 | * Created by thom on 2016/10/23. 5 | */ 6 | public class ProfilerInfo { 7 | } 8 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/app/usage/IUsageStatsManager.java: -------------------------------------------------------------------------------- 1 | package android.app.usage; 2 | 3 | public interface IUsageStatsManager { 4 | 5 | public void setAppInactive(String packageName, boolean inactive, int userId) throws android.os.RemoteException; 6 | 7 | public boolean isAppInactive(String packageName, int userId) throws android.os.RemoteException; 8 | 9 | public static abstract class Stub { 10 | 11 | public static android.app.usage.IUsageStatsManager asInterface(android.os.IBinder obj) { 12 | throw new UnsupportedOperationException(); 13 | } 14 | 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/content/IIntentReceiver.java: -------------------------------------------------------------------------------- 1 | package android.content; 2 | 3 | /** 4 | * Created by thom on 2016/10/23. 5 | */ 6 | public class IIntentReceiver { 7 | } 8 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/content/pm/IPackageManager.java: -------------------------------------------------------------------------------- 1 | package android.content.pm; 2 | 3 | import android.content.ComponentName; 4 | 5 | public interface IPackageManager { 6 | 7 | boolean isProtectedBroadcast(String actionName); 8 | 9 | /* since api-16 */ 10 | ServiceInfo getServiceInfo(ComponentName className, int flags, int userId); 11 | 12 | int getPackageUid(String packageName, int flags, int userId); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/content/pm/PackageParser.java: -------------------------------------------------------------------------------- 1 | package android.content.pm; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * @hide 7 | */ 8 | public class PackageParser { 9 | 10 | public static class IntentInfo { 11 | } 12 | 13 | public static class ActivityIntentInfo extends IntentInfo { 14 | public Activity activity; 15 | } 16 | 17 | public static class ServiceIntentInfo extends IntentInfo { 18 | public Service service; 19 | } 20 | 21 | public static class Component { 22 | public Package owner; 23 | public String className; 24 | } 25 | 26 | public static class Activity extends Component { 27 | public ActivityInfo info; 28 | } 29 | 30 | public static class Service extends Component { 31 | } 32 | 33 | public static class Package { 34 | public ApplicationInfo applicationInfo; 35 | public ArrayList activities; 36 | public ArrayList receivers; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/os/Process.java: -------------------------------------------------------------------------------- 1 | package android.os; 2 | 3 | public class Process { 4 | 5 | public static final int SYSTEM_UID = 1000; 6 | 7 | /** 8 | * @hide 9 | */ 10 | public static final void readProcLines(String path, String[] reqFields, long[] outSizes) { 11 | throw new UnsupportedOperationException(); 12 | } 13 | 14 | /** 15 | * @hide 16 | */ 17 | public static final int getUidForPid(int pid) { 18 | throw new UnsupportedOperationException(); 19 | } 20 | 21 | public static final void killProcess(int pid) { 22 | throw new UnsupportedOperationException(); 23 | } 24 | 25 | public static final int myUid() { 26 | throw new UnsupportedOperationException(); 27 | } 28 | 29 | public static final int myPid() { 30 | throw new UnsupportedOperationException(); 31 | } 32 | 33 | public static final int myTid() { 34 | throw new UnsupportedOperationException(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/os/ServiceManager.java: -------------------------------------------------------------------------------- 1 | package android.os; 2 | 3 | public final class ServiceManager { 4 | public static IBinder getService(String name) { 5 | throw new UnsupportedOperationException(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/os/SystemProperties.java: -------------------------------------------------------------------------------- 1 | package android.os; 2 | 3 | /** 4 | * {@hide} 5 | */ 6 | public class SystemProperties { 7 | 8 | public static String get(String key) { 9 | throw new UnsupportedOperationException(); 10 | } 11 | 12 | public static void set(String key, String val) { 13 | throw new UnsupportedOperationException(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/android/view/IApplicationToken.java: -------------------------------------------------------------------------------- 1 | package android.view; 2 | 3 | /** 4 | * Created by thom on 2016/10/23. 5 | */ 6 | 7 | public interface IApplicationToken { 8 | class Stub { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/com/android/internal/app/IAppOpsService.java: -------------------------------------------------------------------------------- 1 | package com.android.internal.app; 2 | 3 | public interface IAppOpsService { 4 | 5 | void setMode(int code, int uid, String packageName, int mode) throws android.os.RemoteException; 6 | 7 | class Stub { 8 | 9 | public static com.android.internal.app.IAppOpsService asInterface(android.os.IBinder binder) { 10 | throw new UnsupportedOperationException(); 11 | } 12 | 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/com/android/server/am/ActivityRecord.java: -------------------------------------------------------------------------------- 1 | package com.android.server.am; 2 | 3 | import android.os.IBinder; 4 | 5 | /** 6 | * Created by thom on 2016/10/15. 7 | */ 8 | public class ActivityRecord { 9 | String packageName; 10 | 11 | static ActivityRecord forTokenLocked(IBinder token) { 12 | throw new UnsupportedOperationException(); 13 | } 14 | 15 | static ActivityRecord forToken(IBinder token) { 16 | throw new UnsupportedOperationException(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/com/android/server/am/ProcessRecord.java: -------------------------------------------------------------------------------- 1 | package com.android.server.am; 2 | 3 | import android.content.pm.ApplicationInfo; 4 | 5 | /** 6 | * Created by thom on 2016/10/15. 7 | */ 8 | class ProcessRecord { 9 | 10 | ApplicationInfo info; 11 | 12 | boolean killedByAm; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/AndroidHiddenAPI/com/android/server/am/TaskRecord.java: -------------------------------------------------------------------------------- 1 | package com.android.server.am; 2 | 3 | import android.content.Intent; 4 | 5 | /** 6 | * Created by thom on 2016/10/23. 7 | */ 8 | final class TaskRecord { 9 | 10 | Intent intent; 11 | 12 | Intent affinityIntent; 13 | 14 | Intent getBaseIntent() { 15 | throw new UnsupportedOperationException(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 39 | 40 | 45 | 46 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/DEVELOP.md: -------------------------------------------------------------------------------- 1 | # English 2 | For APK, please use `gradle clean aR`. 3 | You can specify the key in file `ant.properties` (please create it) like this: 4 | ``` 5 | key.store= 6 | key.alias= 7 | key.store.password= 8 | key.alias.password= 9 | ``` 10 | 11 | Please note, there is no `framework` part. I won't release the source code for `framework` until original Prevent Running supports Android 7.0+. 12 | 13 | # 中文 14 | 如果要生成 APK 的话,请使用命令`gradle clean aR`。 15 | 你可以在文件`ant.properties`中使用以下参数来指定分发密钥: 16 | ``` 17 | key.store= 18 | key.alias= 19 | key.store.password= 20 | key.alias.password= 21 | ``` 22 | 23 | 注意:源码中没有`framework`,在原始`阻止运行`支持 Android 7.0 之前,不会开放 `framework` 代码。 24 | -------------------------------------------------------------------------------- /app/LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 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 |
  1. Patch your ROM with "Brevent".
  2. 39 |
  3. Open "Brevent", then add/remove application to/from prevent list.
  4. 40 |
  5. Use android normally, press back or remove it from recent task to exit, and press HOME for pause.
  6. 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 all apps (default show third party apps and gapps)
  • 50 |
  • -s for system apps
  • 51 |
  • -e for enabled apps
  • 52 |
  • -r for running apps
  • 53 |
  • -g for gapps, i.e. apps from google
  • 54 |
  • -sg for system apps excluding gapps
  • 55 |
56 | 57 |

Importance for Processes:

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 |
  1. 给系统打“黑域”补丁。
  2. 42 |
  3. 重启后,打开“黑域”,配置阻止列表(这个只需要一次)。
  4. 43 |
  5. 正常使用手机,临时退出时按HOME,不用时按返回键退出或者从最近列表划掉。
  6. 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 | 3 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/res/menu/drawer_nv.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /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" --------------------------------------------------------------------------------