├── .gitignore ├── ChangeLog.md ├── LICENSE ├── NOTICE ├── README.md ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── assets └── xposed_init ├── java ├── at │ └── jclehner │ │ └── appopsxposed │ │ ├── ApkVariant.java │ │ ├── AppListFragment.java │ │ ├── AppOpsActivity.java │ │ ├── AppOpsXposed.java │ │ ├── AppOpsXposedApp.java │ │ ├── Backup.java │ │ ├── Hack.java │ │ ├── IconPreference.java │ │ ├── LauncherActivity.java │ │ ├── ResourcesXposed.java │ │ ├── SettingsActivity.java │ │ ├── SystemEventReceiver.java │ │ ├── hacks │ │ ├── BootCompletedHack.java │ │ ├── DontGroupOpsHack.java │ │ ├── FixOpsPruneHack.java │ │ ├── FixWakeLock.java │ │ ├── GeneralHacks.java │ │ ├── GmsLocationHack.java │ │ └── MiuiHacks.java │ │ ├── util │ │ ├── AppOpsManagerWrapper.java │ │ ├── Constants.java │ │ ├── ExceptionEater.java │ │ ├── ObjectWrapper.java │ │ ├── OpsLabelHelper.java │ │ ├── Res.java │ │ ├── Util.java │ │ └── XUtils.java │ │ └── variants │ │ ├── AOSP.java │ │ ├── CyanogenMod.java │ │ ├── HTC.java │ │ ├── LG.java │ │ ├── Minimal.java │ │ ├── OmniROM.java │ │ ├── Oppo.java │ │ ├── Samsung.java │ │ └── Sony.java ├── com │ └── android │ │ └── settings │ │ └── applications │ │ ├── AppOpsCategory.java │ │ ├── AppOpsDetails.java │ │ ├── AppOpsState.java │ │ └── AppOpsSummary.java └── de │ └── robv │ └── android │ └── xposed │ └── XC_MethodHookRecursive.java └── res ├── drawable-v19 └── ic_appops_sense.xml ├── drawable-xhdpi ├── checkerboard.png ├── checkerboard_framed.png ├── ic_appops_black.png ├── ic_appops_cog_black.png ├── ic_appops_cog_circle.png ├── ic_appops_cog_grey.png ├── ic_appops_cog_teal.png ├── ic_appops_cog_white.png ├── ic_appops_shield_circle.png ├── ic_appops_shield_teal.png ├── ic_appops_white.png └── ic_launcher2.png ├── drawable ├── ic_appops_sense.xml └── ic_launcher.png ├── layout ├── app_ops_details.xml ├── app_ops_details_item.xml ├── app_ops_item.xml ├── app_ops_summary.xml ├── checkable_text.xml ├── icon_dropdown.xml ├── icon_spinner.xml ├── launcher_info.xml ├── manage_applications_item.xml ├── settings.xml └── spinner.xml ├── values-af └── extracted.xml ├── values-am └── extracted.xml ├── values-ar └── extracted.xml ├── values-az-rAZ └── extracted.xml ├── values-be └── extracted.xml ├── values-bg └── extracted.xml ├── values-bn-rBD └── extracted.xml ├── values-ca └── extracted.xml ├── values-cs └── extracted.xml ├── values-da └── extracted.xml ├── values-de ├── extracted.xml └── strings.xml ├── values-el └── extracted.xml ├── values-en-rGB └── extracted.xml ├── values-en-rIN └── extracted.xml ├── values-es ├── extracted.xml └── strings.xml ├── values-et-rEE └── extracted.xml ├── values-eu-rES └── extracted.xml ├── values-fa └── extracted.xml ├── values-fi └── extracted.xml ├── values-fr └── extracted.xml ├── values-gl-rES └── extracted.xml ├── values-hi └── extracted.xml ├── values-hr └── extracted.xml ├── values-hu └── extracted.xml ├── values-hy-rAM └── extracted.xml ├── values-in └── extracted.xml ├── values-is-rIS └── extracted.xml ├── values-it └── extracted.xml ├── values-iw └── extracted.xml ├── values-ja ├── extracted.xml └── strings.xml ├── values-ka-rGE └── extracted.xml ├── values-kk-rKZ └── extracted.xml ├── values-km-rKH └── extracted.xml ├── values-kn-rIN └── extracted.xml ├── values-ko ├── extracted.xml └── strings.xml ├── values-ku └── extracted.xml ├── values-ky-rKG └── extracted.xml ├── values-lb └── extracted.xml ├── values-lo-rLA └── extracted.xml ├── values-lt └── extracted.xml ├── values-lv └── extracted.xml ├── values-mk-rMK └── extracted.xml ├── values-ml-rIN └── extracted.xml ├── values-mn-rMN └── extracted.xml ├── values-mr-rIN └── extracted.xml ├── values-ms-rMY └── extracted.xml ├── values-my-rMM └── extracted.xml ├── values-nb └── extracted.xml ├── values-ne-rNP └── extracted.xml ├── values-nl └── extracted.xml ├── values-pl └── extracted.xml ├── values-pt └── extracted.xml ├── values-ro └── extracted.xml ├── values-ru ├── extracted.xml └── strings.xml ├── values-si-rLK └── extracted.xml ├── values-sk ├── extracted.xml └── strings.xml ├── values-sl └── extracted.xml ├── values-sr └── extracted.xml ├── values-sv └── extracted.xml ├── values-sw └── extracted.xml ├── values-ta-rIN └── extracted.xml ├── values-te-rIN └── extracted.xml ├── values-th └── extracted.xml ├── values-tl └── extracted.xml ├── values-tr └── extracted.xml ├── values-ug └── extracted.xml ├── values-uk └── extracted.xml ├── values-ur-rPK └── extracted.xml ├── values-uz-rUZ └── extracted.xml ├── values-v19 └── values.xml ├── values-v22 ├── styles.xml └── values.xml ├── values-vi └── extracted.xml ├── values-zh-rCN ├── extracted.xml └── strings.xml ├── values-zh-rHK └── extracted.xml ├── values-zh-rTW ├── extracted.xml └── strings.xml ├── values-zu └── extracted.xml ├── values ├── arrays.xml ├── id.xml ├── ops.xml ├── strings.xml ├── styles.xml ├── themes.xml └── values.xml └── xml ├── paths.xml └── settings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | release/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | ## AppOpsXposed Re 2 | 3 | ### 1.30.5: 4 | * Rebased to use XSharedLibrary 5 | * Cleaned up obsolete code 6 | * Changed the application package 7 | * Applied Oreo compatibility patches 8 | 9 | ### 1.30.4: 10 | * Applied Nougat compatibility patches 11 | * Updated Android libraries 12 | * Removed debug activity 13 | * Removed libsuperuser 14 | 15 | --- 16 | 17 | ## AppOpsXposed 18 | 19 | ### 1.30.3: 20 | * Fix missing op labels/summaries 21 | * Fix issue in "Show changed" 22 | * Fix OP_BOOT_COMPLETED on Marshmallow 23 | 24 | ### 1.30.2 25 | * Fixed crashes 26 | 27 | ### 1.30.1 28 | * Fixed crash 29 | * Updated Slovak translation 30 | 31 | ### 1.30 32 | * Better support for Marshmallow 33 | * Fix crashes 34 | * Fix label/summary issues 35 | * Fix multiple icons in "App info" screen 36 | 37 | ### 1.29 38 | * Add option to customize appearance (theme, icons) 39 | * Fixed issue on MiUi where ops were reset 40 | * Add AppOps to AOSP Lollipop Settings 41 | * Add option to export/import app restrictions 42 | * AOX now displays notifications on app installs/updates, 43 | launching the AppOps screen for that particular app 44 | 45 | ### 1.28.1 46 | * Fix bugs in hacks for OP_WAKE_LOCK and OP_BOOT_COMPLETED 47 | 48 | ### 1.28 49 | * Fix crashes on certain HTC devices 50 | * Fix switch labels 51 | * Smaller icon in Settings 52 | 53 | ### 1.27 54 | * Add translations from CM-12 for a ton of languages 55 | 56 | ### 1.26.1 57 | * Restrictions can be reset under "Show changed" 58 | 59 | ### 1.26 60 | * Fix crash when changing ops 61 | * Better handling of non-AOSP ops (labels, summaries) 62 | * Fix issue on Sony ROMS where all ops are off by default 63 | 64 | ### 1.25.3 65 | * Fix several Lollipop issues 66 | 67 | ### 1.24 68 | * Add option to disable verbose logs 69 | * Potential fix for LG icon issue 70 | * Potential fix for Samsung GridSettings bug 71 | * Add hack to fix ops resetting on reboot if 72 | installed on SD. 73 | 74 | ### 1.23 75 | * Finally got the HTC variant working 76 | (thanks to Mikanoshi@XDA) 77 | * Fixed some system-app-install issues 78 | * Bugfixes 79 | 80 | ### 1.22 81 | * AppOpsXposed now works without Xposed, by installing 82 | as a system-app. 83 | * Add new ops for Lollipop (compatibility mode) 84 | * When enabled, use compatibility mode when launching 85 | from settings app as well 86 | 87 | ### 1.21.1 88 | * Fix compatibility mode 89 | 90 | ### 1.21 91 | * Added new compatibility mode (BETA) 92 | * Fix crash on LG ROMs 93 | * Attempted to fix wrong icon size on some ROMs 94 | * WakeLockFix disabled on JellyBean for now 95 | 96 | ### 1.20.2 97 | * Fix version number (updates should work now) 98 | 99 | ### 1.20.1 100 | * Fix settings icon 101 | 102 | ### 1.20 103 | * Fixed compatibility with some LG ROMs 104 | * Fixed WakeLockFix (JellyBean currently broken) 105 | * Updated icon in settings for non-AOSP ROMs 106 | 107 | ### 1.19 108 | * Fixed some issues in Samsung ROMs 109 | * Fixed issue in detection of CyanogenMod-based ROMs 110 | * Added bug report functionality 111 | 112 | ### 1.18 113 | * Added OP_BOOT_COMPLETED hack (MUST BE ENABLED MANUALLY) 114 | * Added WakeLock-fix hack (MUST BE ENABLED MANUALLY) 115 | * Fixed crashes in CyanogenMod-based ROMs 116 | * Added Spanish translation (Jose Artuñedo@XDA) 117 | 118 | ### 1.17.2 119 | * Xperia only: more human readable info 120 | (e.g. "Run at start-up" vs "BOOT_COMPLETED") 121 | 122 | ### 1.17: 123 | * Fixed compatibility with Galaxy S5 settings app 124 | (grid layout) 125 | * Fixed crash on Xperia KitKat ROMs 126 | 127 | ### 1.16: 128 | * Added OmniROM variant (no header in settings, only 129 | icon in "App info") 130 | * Added variant for Sony KitKat ROMs with a switch 131 | in AppOps, as opposed to the drop-down menu found 132 | in 4.3 ROMs. 133 | 134 | ### 1.15.1: 135 | * Fix crashes on Android 4.3 136 | 137 | ### 1.15: 138 | * Added module-specific settings (click module name in 139 | Xposed Installer's "Module" section) 140 | * Launcher icon can now be hidden 141 | * Added Korean translation by @SDKoongchi 142 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | AppOpsXposed Re - AppOps for Android 4.3+ 2 | 3 | Based on: 4 | AppOpsXposed - AppOps for Android 4.3+ 5 | Copyright 2013-2016 Joseph C. Lehner 6 | 7 | AppOpsXposed Re uses code licensed under the Apache Source License: 8 | 9 | * AppOps{Category,Details,State,Summary}.java 10 | Copyright (C) 2013 The Android Open Source Project 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AppOpsXposed Re 2 | ============ 3 | 4 | For information see the Xposed Module Repository: [AppOpsXposed Re](http://repo.xposed.info/module/at.jclehner.appopsxposed.re) 5 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion exCompileSdkVersion 5 | buildToolsVersion exBuildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId "at.jclehner.appopsxposed.re" 9 | minSdkVersion 18 10 | targetSdkVersion 28 11 | versionCode 13006 12 | versionName '1.30.6' 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled true 18 | shrinkResources true 19 | useProguard true 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compileOnly project(':hax') 27 | compileOnly 'de.robv.android.xposed:api:+' 28 | 29 | implementation project(':library') 30 | } 31 | -------------------------------------------------------------------------------- /proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep public class at.jclehner.appopsxposed.AppOpsXposed 2 | -keep public class at.jclehner.appopsxposed.ResourcesXposed 3 | 4 | -keep public class * extends at.jclehner.appopsxposed.ApkVariant 5 | 6 | -keep public class * extends at.jclehner.appopsxposed.Hack 7 | 8 | -keep public class at.jclehner.appopsxposed.util.Util { 9 | public static boolean isBootCompletedHackWorking(); 10 | } 11 | 12 | -keepattributes InnerClasses 13 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 17 | 20 | 23 | 24 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 59 | 60 | 61 | 62 | 63 | 64 | 67 | 70 | 73 | 76 | 77 | 78 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | at.jclehner.appopsxposed.AppOpsXposed 2 | at.jclehner.appopsxposed.ResourcesXposed 3 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/AppOpsActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013-2015 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed; 20 | 21 | import android.app.AlertDialog; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.os.Bundle; 25 | 26 | import androidx.appcompat.app.AppCompatActivity; 27 | import androidx.core.content.ContextCompat; 28 | import androidx.fragment.app.Fragment; 29 | import androidx.fragment.app.FragmentManager; 30 | 31 | import com.android.settings.applications.AppOpsDetails; 32 | import com.android.settings.applications.AppOpsSummary; 33 | 34 | import at.jclehner.appopsxposed.re.R; 35 | import at.jclehner.appopsxposed.util.Util; 36 | import xeed.library.common.SettingsManager; 37 | import xeed.library.common.Utils; 38 | import xeed.library.ui.BaseSettings; 39 | 40 | public class AppOpsActivity extends AppCompatActivity { 41 | private int mThemeId; 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | 47 | SettingsManager.getInstance(this); 48 | 49 | Context ctx = ContextCompat.createDeviceProtectedStorageContext(this); 50 | if (ctx == null) ctx = this; 51 | 52 | BaseSettings.reloadThemes(ctx.getSharedPreferences(Utils.PREFS_NAME, MODE_PRIVATE)); 53 | setTheme(mThemeId = BaseSettings.getActTh()); 54 | 55 | if (!Util.hasAppOpsPermissions(this)) { 56 | AlertDialog.Builder ab = new AlertDialog.Builder(this); 57 | ab.setMessage(getString(R.string.permissions_not_granted, 58 | getString(R.string.app_ops_settings), 59 | getString(R.string.compatibility_mode_title))); 60 | ab.setPositiveButton(android.R.string.ok, null); 61 | ab.show(); 62 | } 63 | 64 | if (savedInstanceState == null) { 65 | Intent in = getIntent(); 66 | String pkg = in.getStringExtra(AppOpsDetails.ARG_PACKAGE_NAME); 67 | Fragment frag; 68 | if (pkg != null) { 69 | frag = new AppOpsDetails(); 70 | frag.setArguments(in.getExtras()); 71 | } else { 72 | frag = new AppOpsSummary(); 73 | } 74 | getSupportFragmentManager().beginTransaction().replace(android.R.id.content, frag).commit(); 75 | } 76 | } 77 | 78 | @Override 79 | public void onResume() { 80 | super.onResume(); 81 | if (mThemeId != BaseSettings.getActTh()) recreate(); 82 | } 83 | 84 | @Override 85 | public void onBackPressed() { 86 | FragmentManager mgr = getSupportFragmentManager(); 87 | if (mgr.getBackStackEntryCount() == 0) { 88 | super.onBackPressed(); 89 | } else { 90 | mgr.popBackStack(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/AppOpsXposed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013-2015 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed; 20 | 21 | import android.app.AndroidAppHelper; 22 | import android.content.Intent; 23 | 24 | import java.io.IOException; 25 | 26 | import at.jclehner.appopsxposed.re.BuildConfig; 27 | import at.jclehner.appopsxposed.util.Res; 28 | import at.jclehner.appopsxposed.util.Util; 29 | import de.robv.android.xposed.XC_MethodHook; 30 | import de.robv.android.xposed.XposedBridge; 31 | import de.robv.android.xposed.XposedHelpers; 32 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 33 | import xeed.library.xposed.BaseModule; 34 | 35 | public class AppOpsXposed extends BaseModule { 36 | public static final String MODULE_PACKAGE = "at.jclehner.appopsxposed.re"; 37 | public static final String SETTINGS_PACKAGE = "com.android.settings"; 38 | public static final String SETTINGS_MAIN_ACTIVITY = SETTINGS_PACKAGE + ".Settings"; 39 | public static final String APP_OPS_FRAGMENT = "com.android.settings.applications.AppOpsSummary"; 40 | static final String APP_OPS_DETAILS_FRAGMENT = "com.android.settings.applications.AppOpsDetails"; 41 | 42 | static { 43 | Util.logger = new Util.Logger() { 44 | 45 | @Override 46 | public void log(Throwable t) { 47 | XposedBridge.log("AOX: " + t); 48 | } 49 | 50 | @Override 51 | public void log(String s) { 52 | XposedBridge.log("AOX: " + s); 53 | } 54 | }; 55 | } 56 | 57 | @Override 58 | protected final String getLogTag() { 59 | return "AOX"; 60 | } 61 | 62 | @Override 63 | public final long getVersion() { 64 | return BuildConfig.VERSION_CODE; 65 | } 66 | 67 | @Override 68 | protected final void reloadPrefs(Intent in) { 69 | } 70 | 71 | @Override 72 | public final String getModulePackage() { 73 | return MODULE_PACKAGE; 74 | } 75 | 76 | @Override 77 | public void initZygote(StartupParam param) throws IOException { 78 | super.initZygote(param); 79 | //if (Res.modRes == null) { 80 | // AssetManager assets = (AssetManager) XposedHelpers.newInstance(AssetManager.class); 81 | // XposedHelpers.callMethod(assets, "addAssetPath", param.modulePath); 82 | // Res.modRes = new Resources(assets, null, null); 83 | // XposedHelpers.callStaticMethod(AndroidAppHelper.class, "addActiveResource", 84 | // new Class[]{String.class, Float.TYPE, Boolean.TYPE, Resources.class}, 85 | // param.modulePath, Res.modRes.hashCode(), false, Res.modRes); 86 | //} 87 | ApkVariant.initVariants(mPrefs); 88 | Hack.initHacks(mPrefs); 89 | } 90 | 91 | @Override 92 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam param) throws Throwable { 93 | super.handleLoadPackage(param); 94 | 95 | boolean isSettings = ApkVariant.isSettingsPackage(param.packageName); 96 | 97 | for (Hack hack : Hack.getAllEnabled(true)) { 98 | try { 99 | hack.handleLoadPackage(param); 100 | } catch (Throwable t) { 101 | log(hack.getClass().getSimpleName() + ": [!!]"); 102 | Util.debug(t); 103 | } 104 | } 105 | 106 | if (!isSettings) 107 | return; 108 | 109 | Class instrumentation = XposedHelpers.findClass("android.app.Instrumentation", param.classLoader); 110 | XposedBridge.hookAllMethods(instrumentation, "newActivity", new XC_MethodHook() { 111 | @Override 112 | protected void afterHookedMethod(MethodHookParam param) { 113 | if (Res.settingsRes == null) { 114 | Res.settingsRes = AndroidAppHelper.currentApplication().getResources(); 115 | } 116 | } 117 | }); 118 | 119 | String forceVariant = mPrefs.getString("force_variant", ""); 120 | for (ApkVariant variant : ApkVariant.getAllMatching(param, forceVariant)) { 121 | String variantName = " " + variant.getClass().getSimpleName(); 122 | 123 | try { 124 | variant.handleLoadPackage(param); 125 | log(variantName + ": [OK]"); 126 | break; 127 | } catch (Throwable t) { 128 | Util.debug(variantName + ": [!!]"); 129 | Util.debug(t); 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/AppOpsXposedApp.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed; 2 | 3 | import android.app.Application; 4 | 5 | public class AppOpsXposedApp extends Application { 6 | 7 | } -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/Hack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed; 20 | 21 | import android.content.Context; 22 | import android.content.SharedPreferences; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import at.jclehner.appopsxposed.hacks.BootCompletedHack; 28 | import at.jclehner.appopsxposed.hacks.DontGroupOpsHack; 29 | import at.jclehner.appopsxposed.hacks.FixOpsPruneHack; 30 | import at.jclehner.appopsxposed.hacks.FixWakeLock; 31 | import at.jclehner.appopsxposed.hacks.GeneralHacks; 32 | import at.jclehner.appopsxposed.hacks.MiuiHacks; 33 | import at.jclehner.appopsxposed.re.BuildConfig; 34 | import at.jclehner.appopsxposed.util.Util; 35 | import de.robv.android.xposed.IXposedHookLoadPackage; 36 | import de.robv.android.xposed.IXposedHookZygoteInit; 37 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 38 | 39 | public abstract class Hack implements IXposedHookLoadPackage, IXposedHookZygoteInit { 40 | public static class PreferenceInfo { 41 | public final boolean defaultValue = false; 42 | public final String key; 43 | public final String title; 44 | public final String summary; 45 | 46 | PreferenceInfo(String key, String title, String summary) { 47 | this.key = key; 48 | this.title = title; 49 | this.summary = summary; 50 | } 51 | } 52 | 53 | private static Hack[] HACKS = null; 54 | 55 | public static void initHacks(SharedPreferences prefs) { 56 | HACKS = new Hack[]{ 57 | new BootCompletedHack(prefs), 58 | new FixWakeLock(prefs), 59 | new FixOpsPruneHack(prefs), 60 | //new GmsLocationHack(prefs), 61 | BuildConfig.DEBUG ? new DontGroupOpsHack(prefs) : null, 62 | new MiuiHacks(prefs), 63 | new GeneralHacks(prefs) 64 | }; 65 | } 66 | 67 | private String mLogTag; 68 | private ClassLoader mClassLoader; 69 | protected final SharedPreferences mPrefs; 70 | 71 | public static List getAllEnabled(boolean quiet) { 72 | List hacks = new ArrayList<>(); 73 | 74 | if (!quiet) 75 | Util.log("Enabled hacks:"); 76 | 77 | for (Hack hack : HACKS) { 78 | if (hack == null) 79 | continue; 80 | 81 | if (hack.mPrefs.getBoolean(hack.getKey(), hack.isEnabledByDefault())) { 82 | hacks.add(hack); 83 | if (!quiet) 84 | Util.log(" " + hack.getClass().getSimpleName()); 85 | } 86 | } 87 | 88 | return hacks; 89 | } 90 | 91 | public Hack(SharedPreferences prefs) { 92 | mPrefs = prefs; 93 | } 94 | 95 | @Override 96 | public void initZygote(StartupParam startupParam) throws Throwable { 97 | 98 | } 99 | 100 | @Override 101 | public final void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 102 | mClassLoader = lpparam.classLoader; 103 | 104 | handleLoadAnyPackage(lpparam); 105 | 106 | if ("android".equals(lpparam.packageName)) 107 | handleLoadFrameworkPackage(lpparam); 108 | else if (AppOpsXposed.SETTINGS_PACKAGE.equals(lpparam.packageName)) 109 | handleLoadSettingsPackage(lpparam); 110 | 111 | } 112 | 113 | public final PreferenceInfo getPrefernceInfo(Context context) { 114 | return new PreferenceInfo(getKey(), onGetPreferenceTitle(context), onGetPreferenceSummary(context)); 115 | } 116 | 117 | protected void handleLoadFrameworkPackage(LoadPackageParam lpparam) throws Throwable { 118 | 119 | } 120 | 121 | protected void handleLoadSettingsPackage(LoadPackageParam lpparam) throws Throwable { 122 | 123 | } 124 | 125 | protected void handleLoadAnyPackage(LoadPackageParam lpparam) throws Throwable { 126 | 127 | } 128 | 129 | protected Class loadClass(String className) throws ClassNotFoundException { 130 | return mClassLoader.loadClass(className); 131 | } 132 | 133 | protected String onGetPreferenceTitle(Context context) { 134 | return getPreferenceString(context, "title"); 135 | } 136 | 137 | protected String onGetPreferenceSummary(Context context) { 138 | return getPreferenceString(context, "summary"); 139 | } 140 | 141 | protected boolean isEnabledByDefault() { 142 | return false; 143 | } 144 | 145 | protected abstract String onGetKeySuffix(); 146 | 147 | protected final void log(String message) { 148 | Util.log(getLogPrefix() + message); 149 | } 150 | 151 | protected final void debug(String message) { 152 | Util.debug(getLogPrefix() + message); 153 | } 154 | 155 | protected final void debug(Throwable t) { 156 | Util.debug(t); 157 | } 158 | 159 | protected final String getPreferenceString(Context context, String suffix, Object... formatArgs) { 160 | String name = "use_hack_" + onGetKeySuffix() + "_" + suffix; 161 | int resId = context.getResources().getIdentifier( 162 | name, "string", AppOpsXposed.MODULE_PACKAGE); 163 | 164 | if (resId != 0) 165 | return context.getString(resId, formatArgs); 166 | 167 | return name; 168 | } 169 | 170 | private String getKey() { 171 | return "use_hack_" + onGetKeySuffix(); 172 | } 173 | 174 | private String getLogPrefix() { 175 | if (mLogTag == null) { 176 | String name = getClass().getName().replace('$', '.'); 177 | String pkgName = getClass().getPackage().getName(); 178 | 179 | if (name.startsWith(pkgName)) 180 | mLogTag = "AOX:" + name.substring(pkgName.length() + 1) + ": "; 181 | else 182 | mLogTag = "AOX:" + name + ": "; 183 | } 184 | 185 | return mLogTag; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/IconPreference.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Rect; 7 | import android.util.AttributeSet; 8 | import android.view.LayoutInflater; 9 | import android.view.TouchDelegate; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.view.ViewParent; 13 | import android.widget.AdapterView; 14 | import android.widget.BaseAdapter; 15 | import android.widget.ImageView; 16 | import android.widget.Spinner; 17 | import android.widget.SpinnerAdapter; 18 | 19 | import androidx.annotation.LayoutRes; 20 | import androidx.preference.Preference; 21 | import androidx.preference.PreferenceViewHolder; 22 | import at.jclehner.appopsxposed.re.R; 23 | 24 | public class IconPreference extends Preference implements AdapterView.OnItemSelectedListener { 25 | private final LayoutInflater mInflater; 26 | private Spinner mSpinner; 27 | private int[] mIcons; 28 | 29 | private int mValue; 30 | private boolean mWasValueSet = false; 31 | 32 | public IconPreference(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | setWidgetLayoutResource(R.layout.spinner); 35 | mInflater = LayoutInflater.from(context); 36 | } 37 | 38 | public void setIcons(int[] icons) { 39 | mIcons = icons; 40 | updateSpinner(); 41 | } 42 | 43 | @Override 44 | protected void onClick() { 45 | super.onClick(); 46 | 47 | if (mSpinner != null) 48 | mSpinner.performClick(); 49 | } 50 | 51 | @Override 52 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 53 | if (callChangeListener(position)) 54 | setValue(position); 55 | } 56 | 57 | @Override 58 | public void onNothingSelected(AdapterView parent) { 59 | 60 | } 61 | 62 | @Override 63 | protected Object onGetDefaultValue(TypedArray a, int index) { 64 | return a.getInt(index, 0); 65 | } 66 | 67 | @SuppressWarnings("deprecation") 68 | @Override 69 | protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 70 | setValue(restoreValue ? getPersistedInt(mValue) : (int) defaultValue); 71 | } 72 | 73 | @Override 74 | public void onBindViewHolder(PreferenceViewHolder view) { 75 | super.onBindViewHolder(view); 76 | mSpinner = (Spinner) view.findViewById(R.id.spinnerWidget); 77 | 78 | ViewParent parent = mSpinner.getParent(); 79 | if (parent instanceof View) { 80 | Rect rect = new Rect(0, 0, ((View) parent).getWidth(), 81 | ((View) parent).getHeight()); 82 | ((View) parent).setTouchDelegate(new TouchDelegate(rect, (View) parent)); 83 | 84 | } 85 | 86 | updateSpinner(); 87 | mSpinner.setOnItemSelectedListener(this); 88 | } 89 | 90 | private void setValue(int value) { 91 | boolean changed = mValue != value; 92 | if (changed || !mWasValueSet) { 93 | mValue = value; 94 | mWasValueSet = true; 95 | persistInt(value); 96 | if (changed) 97 | notifyChanged(); 98 | } 99 | } 100 | 101 | private void updateSpinner() { 102 | if (mSpinner == null) 103 | return; 104 | 105 | mSpinner.setAdapter(null); 106 | mSpinner.setAdapter(mAdapter); 107 | mSpinner.setSelection(mValue); 108 | } 109 | 110 | private final SpinnerAdapter mAdapter = new BaseAdapter() { 111 | @Override 112 | public int getCount() { 113 | return mIcons != null ? mIcons.length : 0; 114 | } 115 | 116 | @Override 117 | public View getView(int position, View convertView, ViewGroup parent) { 118 | return getView(position, R.layout.icon_spinner); 119 | } 120 | 121 | @Override 122 | public View getDropDownView(int position, View convertView, ViewGroup parent) { 123 | View v = getView(position, R.layout.icon_dropdown); 124 | v.setBackgroundResource(position != mValue ? R.drawable.checkerboard 125 | : R.drawable.checkerboard_framed); 126 | return v; 127 | } 128 | 129 | @Override 130 | public long getItemId(int position) { 131 | return position; 132 | } 133 | 134 | @Override 135 | public Object getItem(int position) { 136 | return mIcons[position]; 137 | } 138 | 139 | @SuppressLint("InflateParams") 140 | private ImageView getView(int position, @LayoutRes int layoutResId) { 141 | ImageView view = (ImageView) mInflater.inflate(layoutResId, null, false); 142 | view.setImageResource(mIcons[position]); 143 | return view; 144 | } 145 | }; 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/LauncherActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed; 20 | 21 | import android.app.Activity; 22 | import android.app.AlertDialog; 23 | import android.app.Dialog; 24 | import android.content.Context; 25 | import android.content.DialogInterface; 26 | import android.content.DialogInterface.OnClickListener; 27 | import android.content.Intent; 28 | import android.content.pm.ApplicationInfo; 29 | import android.content.pm.PackageManager; 30 | import android.os.Bundle; 31 | 32 | import androidx.core.content.ContextCompat; 33 | 34 | import at.jclehner.appopsxposed.re.R; 35 | import at.jclehner.appopsxposed.util.Util; 36 | import xeed.library.common.SettingsManager; 37 | import xeed.library.common.Utils; 38 | import xeed.library.ui.BaseSettings; 39 | 40 | public class LauncherActivity extends Activity { 41 | public static class HtcActivity2 extends Activity { 42 | } 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | 48 | SettingsManager.getInstance(this); 49 | 50 | Context ctx = ContextCompat.createDeviceProtectedStorageContext(this); 51 | if (ctx == null) ctx = this; 52 | 53 | BaseSettings.reloadThemes(ctx.getSharedPreferences(Utils.PREFS_NAME, MODE_PRIVATE)); 54 | setTheme(BaseSettings.getActTh()); 55 | 56 | if (checkModuleStatus()) 57 | startActivity(Util.createAppOpsIntent(null)); 58 | } 59 | 60 | // First-launch scenarios: 61 | // 62 | // Running as Xposed module? 63 | // / \ 64 | // NO YES 65 | // | 66 | // Xposed Installed? 67 | // | \ 68 | // YES NO 69 | // | \ 70 | // Install as system app? 71 | 72 | private boolean checkModuleStatus() { 73 | if (BaseSettings.getActiveVer() == -1 && !Util.hasAppOpsPermissions(this)) { 74 | final Intent intent; 75 | String message; 76 | 77 | CharSequence xposedInstallerName = getXposedInstallerName(); 78 | 79 | if (xposedInstallerName != null) { 80 | intent = new Intent("de.robv.android.xposed.installer.OPEN_SECTION"); 81 | intent.putExtra("section", "modules"); 82 | message = getString(R.string.enable_module, xposedInstallerName); 83 | } else { 84 | intent = null; 85 | message = getString(R.string.cannot_enable); 86 | } 87 | 88 | OnClickListener l = new OnClickListener() { 89 | @Override 90 | public void onClick(DialogInterface dialog, int which) { 91 | if (which == Dialog.BUTTON_POSITIVE && intent != null) 92 | startActivity(intent); 93 | 94 | finish(); 95 | } 96 | }; 97 | 98 | AlertDialog.Builder ab = new AlertDialog.Builder(this); 99 | ab.setCancelable(false); 100 | ab.setMessage(message); 101 | ab.setPositiveButton(android.R.string.ok, l); 102 | if (intent != null) 103 | ab.setNegativeButton(android.R.string.cancel, l); 104 | 105 | ab.show(); 106 | return false; 107 | } 108 | 109 | return true; 110 | } 111 | 112 | private CharSequence getXposedInstallerName() { 113 | try { 114 | ApplicationInfo ai = getPackageManager().getApplicationInfo("de.robv.android.xposed.installer", 0); 115 | return ai.loadLabel(getPackageManager()); 116 | } catch (PackageManager.NameNotFoundException e) { 117 | return null; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/ResourcesXposed.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed; 2 | 3 | import android.content.res.XModuleResources; 4 | 5 | import at.jclehner.appopsxposed.util.Res; 6 | import de.robv.android.xposed.IXposedHookInitPackageResources; 7 | import de.robv.android.xposed.IXposedHookZygoteInit; 8 | import de.robv.android.xposed.callbacks.XC_InitPackageResources; 9 | 10 | public class ResourcesXposed implements IXposedHookInitPackageResources, IXposedHookZygoteInit { 11 | @Override 12 | public void initZygote(StartupParam param) { 13 | Res.modRes = XModuleResources.createInstance(param.modulePath, null); 14 | } 15 | 16 | @Override 17 | public void handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam) { 18 | if (ApkVariant.isSettingsPackage(resparam.packageName)) 19 | Res.initializeIcons(resparam); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/SystemEventReceiver.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.app.PendingIntent; 6 | import android.content.BroadcastReceiver; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.content.pm.PackageInfo; 11 | import android.content.pm.PackageManager; 12 | import android.graphics.Bitmap; 13 | import android.graphics.Canvas; 14 | import android.graphics.drawable.BitmapDrawable; 15 | import android.graphics.drawable.Drawable; 16 | import android.net.Uri; 17 | 18 | import androidx.core.content.ContextCompat; 19 | 20 | import at.jclehner.appopsxposed.re.R; 21 | import at.jclehner.appopsxposed.util.Util; 22 | import xeed.library.common.SettingsManager; 23 | import xeed.library.common.Utils; 24 | 25 | public class SystemEventReceiver extends BroadcastReceiver { 26 | @Override 27 | public void onReceive(Context context, Intent intent) { 28 | if (intent.getData() != null && "package".equals(intent.getData().getScheme())) 29 | showPackageNotification(context, intent); 30 | } 31 | 32 | private void showPackageNotification(Context context, Intent intent) { 33 | SettingsManager.getInstance(context); 34 | 35 | Context ctx = ContextCompat.createDeviceProtectedStorageContext(context); 36 | if (ctx == null) ctx = context; 37 | 38 | SharedPreferences sp = ctx.getSharedPreferences(Utils.PREFS_NAME, Context.MODE_PRIVATE); 39 | if (!sp.getBoolean("show_pkg_notifications", true) || !Util.isXposedModuleOrSystemApp(context)) 40 | return; 41 | 42 | PackageManager pm = context.getPackageManager(); 43 | PackageInfo pi = getPkgInfo(context, intent); 44 | if (pi == null || pi.applicationInfo.packageName.equals(context.getPackageName())) 45 | return; 46 | 47 | Notification.Builder nb = new Notification.Builder(context); 48 | nb.setLargeIcon(drawableToBitmap(pi.applicationInfo.loadIcon(pm))); 49 | nb.setSmallIcon(R.drawable.ic_appops_cog_white); 50 | nb.setContentTitle(context.getString(R.string.package_updated)); 51 | nb.setContentText(pi.applicationInfo.loadLabel(pm) + " " + pi.versionName); 52 | nb.setAutoCancel(true); 53 | nb.setPriority(Notification.PRIORITY_LOW); 54 | nb.setContentIntent(PendingIntent.getActivity(context, 0, 55 | Util.createAppOpsIntent(pi.packageName), PendingIntent.FLAG_CANCEL_CURRENT)); 56 | 57 | NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 58 | nm.notify(pi.applicationInfo.uid, nb.build()); 59 | } 60 | 61 | private PackageInfo getPkgInfo(Context context, Intent intent) { 62 | Uri data = intent.getData(); 63 | if (data != null) { 64 | try { 65 | return context.getPackageManager().getPackageInfo( 66 | data.getEncodedSchemeSpecificPart(), 0); 67 | } catch (PackageManager.NameNotFoundException e) { 68 | // ignore 69 | } 70 | } 71 | return null; 72 | } 73 | 74 | private static Bitmap drawableToBitmap(Drawable drawable) { 75 | if (drawable instanceof BitmapDrawable) 76 | return ((BitmapDrawable) drawable).getBitmap(); 77 | 78 | int width = drawable.getIntrinsicWidth(); 79 | width = width > 0 ? width : 1; 80 | int height = drawable.getIntrinsicHeight(); 81 | height = height > 0 ? height : 1; 82 | 83 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 84 | Canvas canvas = new Canvas(bitmap); 85 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 86 | drawable.draw(canvas); 87 | 88 | return bitmap; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/hacks/DontGroupOpsHack.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.hacks; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import at.jclehner.appopsxposed.Hack; 6 | import de.robv.android.xposed.XC_MethodHook; 7 | import de.robv.android.xposed.XposedHelpers; 8 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 9 | 10 | public class DontGroupOpsHack extends Hack { 11 | public DontGroupOpsHack(SharedPreferences prefs) { 12 | super(prefs); 13 | } 14 | 15 | @Override 16 | protected void handleLoadAnyPackage(LoadPackageParam lpparam) { 17 | try { 18 | XposedHelpers.findAndHookMethod("android.app.AppOpsManager", 19 | lpparam.classLoader, "opToSwitch", int.class, new XC_MethodHook() { 20 | 21 | @Override 22 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 23 | param.setResult(param.args[0]); 24 | } 25 | }); 26 | } catch (Throwable t) { 27 | debug(t); 28 | } 29 | } 30 | 31 | @Override 32 | protected String onGetKeySuffix() { 33 | return "dont_group_ops"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/hacks/FixOpsPruneHack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013-2015 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.hacks; 20 | 21 | import android.content.SharedPreferences; 22 | 23 | import at.jclehner.appopsxposed.Hack; 24 | import de.robv.android.xposed.XC_MethodReplacement; 25 | import de.robv.android.xposed.XposedBridge; 26 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 27 | 28 | public class FixOpsPruneHack extends Hack { 29 | public FixOpsPruneHack(SharedPreferences prefs) { 30 | super(prefs); 31 | } 32 | 33 | @Override 34 | protected String onGetKeySuffix() { 35 | return "fix_prune"; 36 | } 37 | 38 | @Override 39 | public void handleLoadFrameworkPackage(LoadPackageParam lpparam) throws Throwable { 40 | Class appOpsSvcClazz = lpparam.classLoader.loadClass("com.android.server.AppOpsService"); 41 | // This is extremely crude, but should suffice for a temporary fix. See 42 | // 9bb3f6785d6 in CyanogenMod/android_frameworks_base for an infinitely more 43 | // elegant solution. 44 | XposedBridge.hookAllMethods(appOpsSvcClazz, "systemReady", XC_MethodReplacement.DO_NOTHING); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/hacks/FixWakeLock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013, 2014 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.hacks; 20 | 21 | import android.annotation.TargetApi; 22 | import android.app.AppOpsManager; 23 | import android.content.Context; 24 | import android.content.SharedPreferences; 25 | import android.os.Binder; 26 | import android.os.Build; 27 | import android.os.IBinder; 28 | 29 | import java.util.Set; 30 | 31 | import at.jclehner.appopsxposed.Hack; 32 | import at.jclehner.appopsxposed.util.AppOpsManagerWrapper; 33 | import at.jclehner.appopsxposed.util.ExceptionEater; 34 | import de.robv.android.xposed.XC_MethodHook; 35 | import de.robv.android.xposed.XC_MethodHook.Unhook; 36 | import de.robv.android.xposed.XposedBridge; 37 | import de.robv.android.xposed.XposedHelpers; 38 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 39 | 40 | 41 | @TargetApi(19) 42 | public class FixWakeLock extends Hack { 43 | private static final boolean ENABLE_PER_TAG_FILTERING = false; 44 | private static final boolean DEBUG = false; 45 | 46 | private static final String POWER_SERVICE = "com.android.server.power.PowerManagerService"; 47 | 48 | public FixWakeLock(SharedPreferences prefs) { 49 | super(prefs); 50 | } 51 | 52 | @Override 53 | protected void handleLoadFrameworkPackage(LoadPackageParam lpparam) throws Throwable { 54 | if (AppOpsManagerWrapper.OP_WAKE_LOCK == -1) { 55 | log("No OP_WAKE_LOCK; bailing out!"); 56 | return; 57 | } 58 | 59 | hookMethods(); 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | @Override 64 | protected void handleLoadAnyPackage(LoadPackageParam lpparam) throws Throwable { 65 | if (AppOpsManagerWrapper.OP_WAKE_LOCK == -1) 66 | return; 67 | 68 | // On some (HTC) ROMs, an IllegalArgumentException thrown by the PowerManagerService 69 | // (because the lock is not actually held) is not ignored in setWorkSource(). 70 | 71 | Class clazz = loadClass("android.os.PowerManager$WakeLock"); 72 | XposedBridge.hookAllMethods(clazz, "setWorkSource", new ExceptionEater(IllegalArgumentException.class)); 73 | } 74 | 75 | @Override 76 | protected String onGetKeySuffix() { 77 | return "wake_lock"; 78 | } 79 | 80 | private static final String ACQUIRE_WAKE_LOCK_CLASS = 81 | POWER_SERVICE + 82 | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 83 | "$BinderService" : ""); 84 | 85 | private void hookMethods() throws Throwable { 86 | Class pwrMgrSvcClazz = loadClass(ACQUIRE_WAKE_LOCK_CLASS); 87 | 88 | Set unhooks = XposedBridge.hookAllMethods(pwrMgrSvcClazz, 89 | "acquireWakeLock", mAcquireHook); 90 | log("Hooked " + unhooks.size() + " functions"); 91 | } 92 | 93 | private boolean canAcquire(String packageName, String tag) { 94 | if (ENABLE_PER_TAG_FILTERING) { 95 | String blacklistKey = "wakelock_hack_is_blacklist/" + packageName; 96 | if (!mPrefs.contains(blacklistKey)) 97 | return false; 98 | 99 | boolean isBlacklist = mPrefs.getBoolean(blacklistKey, true); 100 | Set tags = mPrefs.getStringSet("wakelock_hack_tags/" + packageName, null); 101 | if (tags == null || tags.isEmpty() || !tags.contains(tag)) 102 | return isBlacklist; 103 | 104 | return !isBlacklist; 105 | } 106 | 107 | return false; 108 | } 109 | 110 | private static Context getContextFromThis(Object object) { 111 | Object location = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 112 | XposedHelpers.getSurroundingThis(object) : object; 113 | 114 | return (Context) XposedHelpers.getObjectField(location, "mContext"); 115 | } 116 | 117 | private final XC_MethodHook mAcquireHook = new XC_MethodHook() { 118 | @Override 119 | protected void beforeHookedMethod(MethodHookParam param) { 120 | IBinder lock = (IBinder) param.args[0]; 121 | String tag = (String) param.args[2]; 122 | String packageName = param.args[3] instanceof String ? 123 | (String) param.args[3] : null; 124 | 125 | int uid = Binder.getCallingUid(); 126 | 127 | // Since we want this hack to replicate the expected behaviour, 128 | // we have to do some sanity checks first. On error we return 129 | // and let the hooked function throw an exception. 130 | 131 | if (lock == null || packageName == null) 132 | return; 133 | 134 | Context ctx = getContextFromThis(param.thisObject); 135 | if (ctx == null) 136 | return; 137 | 138 | ctx.enforceCallingOrSelfPermission( 139 | android.Manifest.permission.WAKE_LOCK, null); 140 | 141 | AppOpsManagerWrapper appOps = AppOpsManagerWrapper.from(ctx); 142 | if (appOps.checkOpNoThrow(AppOpsManagerWrapper.OP_WAKE_LOCK, uid, packageName) != AppOpsManager.MODE_ALLOWED) { 143 | if (tag != null && canAcquire(packageName, tag)) { 144 | if (DEBUG) { 145 | log("Allowing acquisition of WakeLock '" + tag + 146 | "' for app " + packageName); 147 | } 148 | return; 149 | } 150 | 151 | if (DEBUG) { 152 | log("Prevented acquisition of WakeLock '" + tag + 153 | "' for app " + packageName); 154 | } 155 | 156 | param.setResult(null); 157 | } 158 | } 159 | }; 160 | } 161 | 162 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/hacks/MiuiHacks.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.hacks; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import at.jclehner.appopsxposed.Hack; 6 | import at.jclehner.appopsxposed.util.Constants; 7 | import at.jclehner.appopsxposed.util.Util; 8 | import de.robv.android.xposed.XC_MethodHook; 9 | import de.robv.android.xposed.XC_MethodReplacement; 10 | import de.robv.android.xposed.XposedBridge; 11 | import de.robv.android.xposed.XposedHelpers; 12 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 13 | 14 | 15 | public class MiuiHacks extends Hack { 16 | public MiuiHacks(SharedPreferences prefs) { 17 | super(prefs); 18 | } 19 | 20 | @Override 21 | protected String onGetKeySuffix() { 22 | return "miui"; 23 | } 24 | 25 | @Override 26 | protected boolean isEnabledByDefault() { 27 | try { 28 | Class.forName("miui.os.Build"); 29 | log("Hack enabled"); 30 | return true; 31 | } catch (ClassNotFoundException e) { 32 | return false; 33 | } 34 | } 35 | 36 | @Override 37 | protected void handleLoadFrameworkPackage(XC_LoadPackage.LoadPackageParam lpparam) { 38 | try { 39 | Class appOpsSvcClazz = loadClass("com.android.server.AppOpsService"); 40 | XposedBridge.hookAllMethods(appOpsSvcClazz, "isMiuiAllowed", 41 | XC_MethodReplacement.returnConstant(true)); 42 | XposedBridge.hookAllMethods(appOpsSvcClazz, "inMiuiAllowedBlackList", 43 | XC_MethodReplacement.returnConstant(false)); 44 | 45 | XposedHelpers.findAndHookMethod(appOpsSvcClazz, "checkSystemApp", 46 | new Class[]{int.class, int.class, String.class}, new XC_MethodHook() { 47 | @Override 48 | protected void afterHookedMethod(MethodHookParam param) { 49 | Boolean result = (Boolean) param.getResult(); 50 | if (result == null || result) 51 | return; 52 | 53 | if (Constants.MODULE_PACKAGE.equals(param.args[2])) 54 | param.setResult(true); 55 | } 56 | } 57 | ); 58 | 59 | log("Hooked: isMiuiAllowed, inMiuiAllowedBlackList, checkSystemApp"); 60 | } catch (Throwable t) { 61 | Util.log(t); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/util/Constants.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.util; 2 | 3 | import at.jclehner.appopsxposed.re.R; 4 | 5 | public final class Constants { 6 | public static final String[] APP_OPS_PERMISSIONS = { 7 | "android.permission.UPDATE_APP_OPS_STATS", 8 | "android.permission.GET_APP_OPS_STATS" 9 | }; 10 | 11 | public static final String MODULE_PACKAGE = "at.jclehner.appopsxposed"; 12 | 13 | public static final int[] ICONS = { 14 | R.drawable.ic_launcher2, 15 | R.drawable.ic_appops_cog_grey, 16 | R.drawable.ic_appops_cog_black, 17 | R.drawable.ic_appops_cog_teal, 18 | R.drawable.ic_appops_cog_white, 19 | R.drawable.ic_appops_cog_circle, 20 | R.drawable.ic_appops_black, 21 | R.drawable.ic_appops_shield_teal, 22 | R.drawable.ic_appops_white, 23 | R.drawable.ic_appops_shield_circle 24 | }; 25 | 26 | public static final int ICON_LAUNCHER = 0; 27 | public static final int ICON_COG_GREY = 1; 28 | public static final int ICON_COG_BLACK = 2; 29 | public static final int ICON_COG_TEAL = 3; 30 | public static final int ICON_COG_WHITE = 4; 31 | public static final int ICON_COG_CIRCLE = 5; 32 | public static final int ICON_SHIELD_BLACK = 6; 33 | public static final int ICON_SHIELD_TEAL = 7; 34 | public static final int ICON_SHIELD_WHITE = 8; 35 | public static final int ICON_SHIELD_CIRCLE = 0; 36 | 37 | private Constants() { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/util/ExceptionEater.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.util; 2 | 3 | 4 | import de.robv.android.xposed.XC_MethodHook; 5 | 6 | public class ExceptionEater extends XC_MethodHook { 7 | private final Object mResult; 8 | private final Class[] mExceptionClasses; 9 | 10 | @SuppressWarnings("unchecked") 11 | public ExceptionEater(Object result, Class... classes) { 12 | mResult = result; 13 | mExceptionClasses = classes; 14 | } 15 | 16 | public ExceptionEater(Class... types) { 17 | this(null, types); 18 | } 19 | 20 | @Override 21 | protected void afterHookedMethod(MethodHookParam param) { 22 | Throwable t = param.getThrowable(); 23 | if (t != null) { 24 | Class tCls = t.getClass(); 25 | 26 | for (Class cls : mExceptionClasses) { 27 | if (cls.isAssignableFrom(tCls)) { 28 | param.setResult(mResult); 29 | return; 30 | } else { 31 | // Retry in case both classes are from different class loaders 32 | try { 33 | if (tCls.getClassLoader().loadClass(cls.getName()).isAssignableFrom(tCls)) { 34 | param.setResult(mResult); 35 | return; 36 | } 37 | } catch (ClassNotFoundException e) { 38 | Util.debug(e); 39 | } catch (ClassCastException e) { 40 | Util.debug(e); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/util/ObjectWrapper.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.util; 2 | 3 | import android.util.Log; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.InvocationTargetException; 7 | import java.lang.reflect.Method; 8 | 9 | 10 | public class ObjectWrapper { 11 | public static class ReflectiveException extends RuntimeException { 12 | private static final long serialVersionUID = 4913462741847291648L; 13 | 14 | public ReflectiveException(Throwable cause) { 15 | super(cause); 16 | } 17 | } 18 | 19 | protected final Object mObj; 20 | 21 | public ObjectWrapper(Object obj) { 22 | mObj = obj; 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | public T get(String fieldName) { 27 | try { 28 | return (T) getDeclaredField(mObj.getClass(), fieldName).get(mObj); 29 | } catch (IllegalAccessException e) { 30 | throw new ReflectiveException(e); 31 | } catch (IllegalArgumentException e) { 32 | throw new ReflectiveException(e); 33 | } catch (NoSuchFieldException e) { 34 | throw new ReflectiveException(e); 35 | } catch (ClassCastException e) { 36 | throw new ReflectiveException(e); 37 | } 38 | } 39 | 40 | public static T getStatic(Class clazz, String fieldName, T defValue) { 41 | try { 42 | return getStatic(clazz, fieldName); 43 | } catch (ReflectiveException e) { 44 | Log.w("ObjectWrapper", e); 45 | return defValue; 46 | } 47 | } 48 | 49 | @SuppressWarnings("unchecked") 50 | public static T getStatic(Class clazz, String fieldName) { 51 | try { 52 | return (T) getDeclaredField(clazz, fieldName).get(null); 53 | } catch (IllegalAccessException e) { 54 | throw new ReflectiveException(e); 55 | } catch (IllegalArgumentException e) { 56 | throw new ReflectiveException(e); 57 | } catch (NoSuchFieldException e) { 58 | throw new ReflectiveException(e); 59 | } catch (ClassCastException e) { 60 | throw new ReflectiveException(e); 61 | } 62 | } 63 | 64 | public void set(String fieldName, Object value) { 65 | try { 66 | getDeclaredField(mObj.getClass(), fieldName).set(mObj, value); 67 | } catch (NoSuchFieldException e) { 68 | throw new ReflectiveException(e); 69 | } catch (IllegalAccessException e) { 70 | throw new ReflectiveException(e); 71 | } catch (IllegalArgumentException e) { 72 | throw new ReflectiveException(e); 73 | } 74 | } 75 | 76 | public T call(String methodName, Object... args) { 77 | return call(methodName, getTypes(args), args); 78 | } 79 | 80 | public T call(String methodName, Class[] parameterTypes, Object... args) { 81 | return callInternal(mObj.getClass(), mObj, methodName, parameterTypes, args); 82 | } 83 | 84 | public T callStatic(String methodName, Object... args) { 85 | return callStatic(methodName, getTypes(args), args); 86 | } 87 | 88 | public T callStatic(String methodName, Class[] parameterTypes, Object... args) { 89 | return callInternal(mObj.getClass(), null, methodName, parameterTypes, args); 90 | } 91 | 92 | public static T callStatic(Class clazz, String methodName, Class[] parameterTypes, Object... args) { 93 | return callInternal(clazz, null, methodName, parameterTypes, args); 94 | } 95 | 96 | public static T callStatic(Class clazz, String methodName, Object... args) { 97 | return callStatic(clazz, methodName, getTypes(args), args); 98 | } 99 | 100 | @SuppressWarnings("unchecked") 101 | private static T callInternal(Class clazz, Object receiver, String methodName, Class[] parameterTypes, Object... args) { 102 | try { 103 | return (T) getDeclaredMethod(clazz, methodName, parameterTypes).invoke(receiver, args); 104 | } catch (NoSuchMethodException e) { 105 | throw new ReflectiveException(e); 106 | } catch (IllegalAccessException e) { 107 | throw new ReflectiveException(e); 108 | } catch (IllegalArgumentException e) { 109 | throw new ReflectiveException(e); 110 | } catch (InvocationTargetException e) { 111 | Throwable targetException = e.getTargetException(); 112 | if (targetException instanceof RuntimeException) 113 | throw (RuntimeException) targetException; 114 | 115 | throw new ReflectiveException(e); 116 | } catch (ClassCastException e) { 117 | throw new ReflectiveException(e); 118 | } 119 | } 120 | 121 | private static Field getDeclaredField(Class clazz, String name) throws NoSuchFieldException { 122 | Field f = clazz.getDeclaredField(name); 123 | f.setAccessible(true); 124 | return f; 125 | } 126 | 127 | private static Method getDeclaredMethod(Class clazz, String name, Class[] parameterTypes) 128 | throws NoSuchMethodException { 129 | Method m = clazz.getDeclaredMethod(name, parameterTypes); 130 | m.setAccessible(true); 131 | return m; 132 | } 133 | 134 | private static Class[] getTypes(Object[] args) { 135 | Class[] types = new Class[args.length]; 136 | 137 | for (int i = 0; i != args.length; ++i) 138 | types[i] = args[i].getClass(); 139 | 140 | return types; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/util/Res.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.util; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageManager; 5 | import android.content.res.Resources; 6 | import android.graphics.drawable.Drawable; 7 | 8 | import androidx.annotation.DrawableRes; 9 | import androidx.annotation.StringRes; 10 | import at.jclehner.appopsxposed.AppOpsXposed; 11 | import de.robv.android.xposed.callbacks.XC_InitPackageResources; 12 | 13 | public class Res { 14 | private static final int[] mIcons = new int[Constants.ICONS.length]; 15 | private static boolean mIconsLoaded; 16 | 17 | public static Resources settingsRes; 18 | public static Resources modRes; 19 | 20 | public static void initializeIcons(XC_InitPackageResources.InitPackageResourcesParam resParam) { 21 | for (int i = 0; i != mIcons.length; ++i) { 22 | mIcons[i] = resParam.res.addResource(Res.modRes, Constants.ICONS[i]); 23 | } 24 | mIconsLoaded = true; 25 | } 26 | 27 | public static int getSettingsIdentifier(String name) { 28 | return settingsRes.getIdentifier(name, null, AppOpsXposed.SETTINGS_PACKAGE); 29 | } 30 | 31 | private static void initializeModRes(PackageManager manager) { 32 | if (modRes != null) return; 33 | try { 34 | modRes = manager.getResourcesForApplication(AppOpsXposed.MODULE_PACKAGE); 35 | } catch (PackageManager.NameNotFoundException e) { 36 | throw new RuntimeException(e); 37 | } 38 | } 39 | 40 | private static void initializeAppRes(PackageManager manager) { 41 | if (settingsRes != null) return; 42 | try { 43 | settingsRes = manager.getResourcesForApplication(AppOpsXposed.MODULE_PACKAGE); 44 | } catch (PackageManager.NameNotFoundException e) { 45 | throw new RuntimeException(e); 46 | } 47 | } 48 | 49 | public static String getSettingsString(Context context, @StringRes int resId) { 50 | initializeAppRes(context.getPackageManager()); 51 | return settingsRes.getString(resId); 52 | } 53 | 54 | public static String getModString(Context context, @StringRes int resId) { 55 | initializeModRes(context.getPackageManager()); 56 | return modRes.getString(resId); 57 | } 58 | 59 | public static Drawable getModDrawable(Context context, @DrawableRes int resId) { 60 | initializeModRes(context.getPackageManager()); 61 | return modRes.getDrawable(resId); 62 | } 63 | 64 | public static int getIcon(int index) { 65 | return mIconsLoaded ? mIcons[index] : android.R.drawable.ic_menu_preferences; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/util/XUtils.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.util; 2 | 3 | import android.content.pm.ApplicationInfo; 4 | 5 | import de.robv.android.xposed.XC_MethodHook; 6 | import de.robv.android.xposed.XC_MethodHook.Unhook; 7 | import de.robv.android.xposed.XC_MethodHookRecursive; 8 | import de.robv.android.xposed.XposedHelpers; 9 | 10 | public final class XUtils { 11 | public static ApplicationInfo getApplicationInfo(String packageName) { 12 | Class atClazz = XposedHelpers.findClass("android.app.ActivityThread", null); 13 | Object pm = XposedHelpers.callStaticMethod(atClazz, "getPackageManager"); 14 | 15 | try { 16 | ApplicationInfo ai = (ApplicationInfo) XposedHelpers.callMethod(pm, 17 | "getApplicationInfo", new Class[]{String.class, int.class, int.class}, 18 | packageName, 0, 0); 19 | 20 | if (ai != null) 21 | return ai; 22 | } catch (Exception e) { 23 | Util.debug(e); 24 | } 25 | 26 | Util.log("getApplicationInfo failed for " + packageName); 27 | 28 | return null; 29 | } 30 | 31 | public static Unhook findAndHookMethodRecursive(String className, ClassLoader classLoader, 32 | String methodName, Object... parameterTypesAndCallback) throws Throwable { 33 | return findAndHookMethodRecursive(classLoader.loadClass(className), methodName, parameterTypesAndCallback); 34 | } 35 | 36 | public static Unhook findAndHookMethodRecursive(Class clazz, String methodName, Object... parameterTypesAndCallback) { 37 | XC_MethodHook hook = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length - 1]; 38 | if (!(hook instanceof XC_MethodHookRecursive)) { 39 | hook = new XC_MethodHookRecursive(hook, clazz); 40 | parameterTypesAndCallback[parameterTypesAndCallback.length - 1] = hook; 41 | } 42 | 43 | try { 44 | return XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback); 45 | } catch (NoSuchMethodError e) { 46 | Class superClass = clazz.getSuperclass(); 47 | if (superClass == null) { 48 | // We can't just rethrow the caught exception since the message would not be 49 | // meaningful (clazz is Object.class at this point). 50 | Class targetClass = ((XC_MethodHookRecursive) hook).getTargetClass(); 51 | throw new NoSuchMethodError(targetClass.getName() + "." + methodName); 52 | } 53 | 54 | Util.debug("findAndHookMethodRecursive: trying " + superClass + "." + methodName); 55 | return findAndHookMethodRecursive(superClass, methodName, parameterTypesAndCallback); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/CyanogenMod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.variants; 20 | 21 | import android.content.SharedPreferences; 22 | import android.content.pm.ApplicationInfo; 23 | 24 | import at.jclehner.appopsxposed.util.Util; 25 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 26 | 27 | public class CyanogenMod extends AOSP { 28 | private static final String CM_VERSION = Util.getSystemProperty("ro.cm.version", ""); 29 | 30 | public CyanogenMod(SharedPreferences prefs) { 31 | super(prefs); 32 | } 33 | 34 | @Override 35 | protected boolean onMatch(ApplicationInfo appInfo, ClassChecker classChecker) { 36 | return CM_VERSION.length() != 0; 37 | } 38 | 39 | @Override 40 | public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 41 | // for now... 42 | super.handleLoadPackage(lpparam); 43 | debug("ro.cm.version=" + CM_VERSION); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/HTC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | 20 | package at.jclehner.appopsxposed.variants; 21 | 22 | import android.app.Activity; 23 | import android.app.Fragment; 24 | import android.content.SharedPreferences; 25 | import android.content.pm.ActivityInfo; 26 | import android.os.Bundle; 27 | import android.view.LayoutInflater; 28 | import android.view.ViewGroup; 29 | 30 | import at.jclehner.appopsxposed.ApkVariant; 31 | import at.jclehner.appopsxposed.AppOpsXposed; 32 | import at.jclehner.appopsxposed.util.Constants; 33 | import de.robv.android.xposed.XC_MethodHook; 34 | import de.robv.android.xposed.XposedHelpers; 35 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 36 | 37 | 38 | /* 39 | * HTC Sense offers a surprisingly easy method to add items to its 40 | * "Settings" app (kudos to Mikanoshi@XDA), by using meta-data in 41 | * AndroidManifest.xml. 42 | * 43 | */ 44 | public class HTC extends ApkVariant { 45 | public static final String APP_OPS_DETAILS_FRAGMENT = 46 | "com.android.settings.framework.activity.application.appops.HtcAppOpsDetails"; 47 | 48 | public HTC(SharedPreferences prefs) { 49 | super(prefs); 50 | } 51 | 52 | @Override 53 | protected String manufacturer() { 54 | return "HTC"; 55 | } 56 | 57 | @Override 58 | public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 59 | addAppOpsToAppInfo(lpparam); 60 | fixLaunchTarget(lpparam); 61 | } 62 | 63 | @Override 64 | protected String[] indicatorClasses() { 65 | return new String[]{ 66 | "com.htc.preference.HtcPreferenceActivity", 67 | "com.htc.preference.HtcPreferenceActivity$Header", 68 | "com.android.settings.framework.activity.HtcWrapHeader", 69 | "com.android.settings.framework.activity.HtcWrapHeaderList", 70 | "com.android.settings.framework.activity.HtcGenericEntryProvider" 71 | }; 72 | } 73 | 74 | @Override 75 | protected String getAppOpsDetailsFragmentName() { 76 | // TODO: use HtcAppOpsDetails if available? 77 | return super.getAppOpsDetailsFragmentName(); 78 | } 79 | 80 | private void fixLaunchTarget(LoadPackageParam lpparam) { 81 | try { 82 | XposedHelpers.findAndHookMethod("com.android.settings.framework.content.plugin.HtcPluginMetadata", lpparam.classLoader, 83 | "fillLaunchTarget", "com.htc.preference.HtcPreferenceActivity.Header", ActivityInfo.class, new XC_MethodHook() { 84 | 85 | @Override 86 | protected void afterHookedMethod(MethodHookParam param) { 87 | try { 88 | Object header = param.args[0]; 89 | if (AppOpsXposed.APP_OPS_FRAGMENT.equals(XposedHelpers.getObjectField(header, "fragment"))) { 90 | Bundle fragmentArguments = new Bundle(); 91 | fragmentArguments.putString("android.intent.extra.PACKAGE_NAME", AppOpsXposed.SETTINGS_PACKAGE); 92 | XposedHelpers.setObjectField(header, "fragmentArguments", fragmentArguments); 93 | } 94 | } catch (Throwable t) { 95 | debug(t); 96 | } 97 | } 98 | }); 99 | 100 | XposedHelpers.findAndHookMethod(AppOpsXposed.APP_OPS_FRAGMENT, lpparam.classLoader, 101 | "onCreateView", LayoutInflater.class, ViewGroup.class, Bundle.class, new XC_MethodHook() { 102 | 103 | @SuppressWarnings("deprecation") 104 | @Override 105 | protected void afterHookedMethod(MethodHookParam param) { 106 | try { 107 | Object mHandlerWrapper = XposedHelpers.callMethod(param.thisObject, "getActivityHandlerWrapper"); 108 | if (mHandlerWrapper != null) { 109 | Activity act = ((Fragment) param.thisObject).getActivity(); 110 | XposedHelpers.callMethod(mHandlerWrapper, "setTitle", act.getResources().getIdentifier("app_ops_settings", "string", AppOpsXposed.SETTINGS_PACKAGE)); 111 | } 112 | } catch (Throwable t) { 113 | debug(t); 114 | } 115 | } 116 | }); 117 | } catch (Throwable t) { 118 | debug(t); 119 | } 120 | } 121 | 122 | @Override 123 | protected int getDefaultAppOpsHeaderIcon() { 124 | return Constants.ICON_COG_GREY; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/LG.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013-2015 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.variants; 20 | 21 | import android.content.Intent; 22 | import android.content.SharedPreferences; 23 | import android.preference.Preference; 24 | import android.preference.PreferenceFragment; 25 | import android.preference.PreferenceScreen; 26 | 27 | import at.jclehner.appopsxposed.AppOpsXposed; 28 | import at.jclehner.appopsxposed.re.R; 29 | import at.jclehner.appopsxposed.util.Constants; 30 | import at.jclehner.appopsxposed.util.Res; 31 | import at.jclehner.appopsxposed.util.Util; 32 | import at.jclehner.appopsxposed.util.XUtils; 33 | import de.robv.android.xposed.XC_MethodHook; 34 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 35 | 36 | /* 37 | * LG decided to go overboard by providing another settings app, 38 | * for some obscure reason called 'easy' settings with a tabbed interface 39 | * rather than the usual list interface. LGSettings.apk is compatible with 40 | * the AOSP variant, LGEasySettings.apk however is mostly 41 | * a new interface, with many preferences actually launching intents to the 42 | * 'regular' com.android.settings package (LGSettings.apk). 43 | */ 44 | public class LG extends AOSP { 45 | private static final String EASY_SETTINGS_PACKAGE = 46 | "com.lge.settings.easy"; 47 | 48 | public LG(SharedPreferences prefs) { 49 | super(prefs); 50 | } 51 | 52 | @Override 53 | protected String manufacturer() { 54 | return "LGE"; 55 | } 56 | 57 | @Override 58 | protected String[] targetPackages() { 59 | return new String[]{AppOpsXposed.SETTINGS_PACKAGE, EASY_SETTINGS_PACKAGE}; 60 | } 61 | 62 | @Override 63 | protected String[] indicatorClasses() { 64 | return new String[]{ 65 | // in com.android.settings 66 | "com.android.settings.lge.DeviceInfoLge", 67 | "com.android.settings.lge.Serial", 68 | // in com.lge.settings.easy 69 | "com.lge.settings.general.EasyGeneralFragment" 70 | }; 71 | } 72 | 73 | @Override 74 | public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 75 | if (!EASY_SETTINGS_PACKAGE.equals(lpparam.packageName)) { 76 | super.handleLoadPackage(lpparam); 77 | return; 78 | } 79 | 80 | XUtils.findAndHookMethodRecursive("com.lge.settings.general.EasyGeneralFragment", lpparam.classLoader, 81 | "addPreferencesFromResource", int.class, new XC_MethodHook() { 82 | 83 | @Override 84 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 85 | debug("addPreferencesFromResource: resId=" + param.args[0]); 86 | 87 | PreferenceFragment pf = ((PreferenceFragment) param.thisObject); 88 | PreferenceScreen ps = pf.getPreferenceScreen(); 89 | int order = Preference.DEFAULT_ORDER; 90 | 91 | for (int i = 0; i != ps.getPreferenceCount(); ++i) { 92 | Intent intent = ps.getPreference(i).getIntent(); 93 | if (intent != null && "android.settings.APPLICATION_SETTINGS" 94 | .equals(intent.getAction())) { 95 | order = i + 1; 96 | break; 97 | } 98 | } 99 | 100 | Preference p = new Preference(pf.getActivity()); 101 | p.setTitle(Res.getModString(pf.getActivity(), R.string.app_ops_settings)); 102 | p.setIcon(getAppOpsHeaderIcon()); 103 | p.setIntent(Util.createAppOpsIntent(null)); 104 | p.setOrder(order); 105 | 106 | ps.addPreference(p); 107 | } 108 | }); 109 | } 110 | 111 | @Override 112 | protected int getDefaultAppOpsHeaderIcon() { 113 | return Constants.ICON_LAUNCHER; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/Minimal.java: -------------------------------------------------------------------------------- 1 | package at.jclehner.appopsxposed.variants; 2 | 3 | 4 | import android.content.SharedPreferences; 5 | import android.content.pm.ApplicationInfo; 6 | 7 | import at.jclehner.appopsxposed.ApkVariant; 8 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 9 | 10 | public class Minimal extends ApkVariant { 11 | public Minimal(SharedPreferences prefs) { 12 | super(prefs); 13 | } 14 | 15 | @Override 16 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { 17 | addAppOpsToAppInfo(lpparam); 18 | } 19 | 20 | @Override 21 | protected boolean onMatch(ApplicationInfo appInfo, ClassChecker classChecker) { 22 | return false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/OmniROM.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.variants; 20 | 21 | import android.content.SharedPreferences; 22 | import android.content.pm.ApplicationInfo; 23 | 24 | import at.jclehner.appopsxposed.util.Util; 25 | 26 | public class OmniROM extends Minimal { 27 | public OmniROM(SharedPreferences prefs) { 28 | super(prefs); 29 | } 30 | 31 | @Override 32 | protected boolean onMatch(ApplicationInfo appInfo, ClassChecker classChecker) { 33 | return !Util.getSystemProperty("ro.omni.version", "").isEmpty(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/Oppo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013-2015 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.variants; 20 | 21 | import android.content.Context; 22 | import android.content.SharedPreferences; 23 | import android.preference.PreferenceActivity.Header; 24 | 25 | import at.jclehner.appopsxposed.AppOpsXposed; 26 | import at.jclehner.appopsxposed.util.Constants; 27 | import at.jclehner.appopsxposed.util.Util; 28 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 29 | 30 | public class Oppo extends AOSP { 31 | private boolean mForceCompatibilityMode = true; 32 | 33 | public Oppo(SharedPreferences prefs) { 34 | super(prefs); 35 | } 36 | 37 | @Override 38 | protected String manufacturer() { 39 | return "OPPO"; 40 | } 41 | 42 | @Override 43 | protected String[] indicatorClasses() { 44 | return new String[]{ 45 | "com.oppo.settings.SettingsActivity" 46 | }; 47 | } 48 | 49 | @Override 50 | protected int getDefaultAppOpsHeaderIcon() { 51 | return Constants.ICON_LAUNCHER; 52 | } 53 | 54 | @Override 55 | public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 56 | try { 57 | lpparam.classLoader.loadClass(AppOpsXposed.APP_OPS_FRAGMENT); 58 | mForceCompatibilityMode = false; 59 | } catch (ClassNotFoundException e) { 60 | log("No " + AppOpsXposed.APP_OPS_FRAGMENT + " in " + lpparam.packageName); 61 | } 62 | 63 | super.handleLoadPackage(lpparam); 64 | } 65 | 66 | @Override 67 | protected Object onCreateAppOpsHeader(Context context, int addAfterHeaderId) { 68 | Header header = (Header) super.onCreateAppOpsHeader(context, addAfterHeaderId); 69 | if (mForceCompatibilityMode) { 70 | header.fragment = null; 71 | header.intent = Util.createAppOpsIntent(null); 72 | } 73 | return header; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/Samsung.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.variants; 20 | 21 | import android.content.SharedPreferences; 22 | 23 | import at.jclehner.appopsxposed.ApkVariant; 24 | import at.jclehner.appopsxposed.AppOpsXposed; 25 | import at.jclehner.appopsxposed.util.Res; 26 | import de.robv.android.xposed.XposedHelpers; 27 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 28 | 29 | public class Samsung extends ApkVariant { 30 | private static final String GRID_SETTINGS = "com.android.settings.GridSettings"; 31 | 32 | public Samsung(SharedPreferences prefs) { 33 | super(prefs); 34 | } 35 | 36 | @Override 37 | protected String manufacturer() { 38 | return "Samsung"; 39 | } 40 | 41 | @Override 42 | public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 43 | addAppOpsToAppInfo(lpparam); 44 | 45 | /* 46 | * In SecSettings.apk, the regular "Manage applications" header is referenced in 47 | * three (or four) xml resources: 48 | * 49 | * xml/settings_headers (also used in stock Android) 50 | * xml/general_headers 51 | * xml/management_headers 52 | * xml/grid_settings (on S5 ROMs) 53 | * 54 | * To keep things simple and not having to determine which of these is actually used, 55 | * we'll hook any call to loadHeadersFromResource and check if the resource argument 56 | * matches any of the three. If it does, we'll just put it right after the 57 | * "Manage applications" header. The chances of more than one being used at the same time 58 | * should be rather slim - if they are, there'll be more than one occurence of "App ops", 59 | * so no biggie. 60 | */ 61 | 62 | int[] xmlHookResIds = { 63 | Res.getSettingsIdentifier("xml/settings_headers"), 64 | Res.getSettingsIdentifier("xml/general_headers"), 65 | Res.getSettingsIdentifier("xml/management_headers"), 66 | Res.getSettingsIdentifier("xml/grid_settings_headers") 67 | }; 68 | 69 | int manageAppsHeaderId = Res.getSettingsIdentifier("id/application_settings"); 70 | 71 | hookLoadHeadersFromResource(lpparam, AppOpsXposed.SETTINGS_MAIN_ACTIVITY, xmlHookResIds, manageAppsHeaderId); 72 | 73 | if (hasGridSettings(lpparam)) 74 | hookLoadHeadersFromResource(lpparam, GRID_SETTINGS, xmlHookResIds, manageAppsHeaderId); 75 | } 76 | 77 | @Override 78 | protected String[] indicatorClasses() { 79 | return new String[]{ 80 | "com.sec.android.samsungapps.util.ServiceBinder", 81 | "com.sec.android.samsungapps.util.PreloadUpdate", 82 | "com.sec.android.touchwiz.widget.TwTouchPunchView", 83 | "com.android.settings.helpdialog.TwTouchPunchView", 84 | GRID_SETTINGS 85 | }; 86 | } 87 | 88 | private boolean hasGridSettings(LoadPackageParam lpparam) { 89 | try { 90 | XposedHelpers.findClass(GRID_SETTINGS, lpparam.classLoader); 91 | return true; 92 | } catch (Throwable t) { 93 | return false; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/at/jclehner/appopsxposed/variants/Sony.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AppOpsXposed - AppOps for Android 4.3+ 3 | * Copyright (C) 2013, 2014 Joseph C. Lehner 4 | * 5 | * This program is free software: you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation, either version 3 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program. If not, see . 17 | */ 18 | 19 | package at.jclehner.appopsxposed.variants; 20 | 21 | 22 | import android.content.SharedPreferences; 23 | 24 | import at.jclehner.appopsxposed.util.Constants; 25 | 26 | 27 | /* 28 | * The Settings app on Sony devices (Xperia ROMs at least) is surprisingly 29 | * vanilla, meaning we can use AOSP.handleLoadPackage(). 30 | * 31 | * However, Sony decided to make the following modifications: 32 | * 33 | * 1) AppOpsSummary can't be launched using the PreferenceActivity.SHOW_FRAGMENT 34 | * extra since the hosting Activity is finish()'ed in AppOpsSummary.onCreateView() 35 | * 36 | * 2) On JellyBean, in layout/app_ops_details_item, the Switch has been replaced by a 37 | * Spinner offering three (!) choices, one of them being "Ask always". 38 | * 39 | * The first issue is dealt with by hooking Activity.finish() for the duration of the 40 | * call to AppOpsSummary.onCreateView(). The second issue is addressed by hooking into 41 | * LayoutInflater.inflate() in AppOpsDetails.refreshUi, where we can hide the Switch 42 | * and show the Spinner. 43 | */ 44 | public abstract class Sony extends AOSP { 45 | // Keep these for now, so we don't break the "force_variant" setting 46 | 47 | public static class JellyBean extends Sony { 48 | public JellyBean(SharedPreferences prefs) { 49 | super(prefs); 50 | } 51 | 52 | @Override 53 | protected int apiLevel() { 54 | return 18; 55 | } 56 | } 57 | 58 | public static class KitKat extends Sony { 59 | public KitKat(SharedPreferences prefs) { 60 | super(prefs); 61 | } 62 | 63 | @Override 64 | protected int apiLevel() { 65 | return 0; 66 | } 67 | } 68 | 69 | public Sony(SharedPreferences prefs) { 70 | super(prefs); 71 | } 72 | 73 | @Override 74 | protected abstract int apiLevel(); 75 | 76 | @Override 77 | protected String manufacturer() { 78 | return "Sony"; 79 | } 80 | 81 | @Override 82 | protected int getDefaultAppOpsHeaderIcon() { 83 | return Constants.ICON_LAUNCHER; 84 | } 85 | 86 | @Override 87 | protected String[] indicatorClasses() { 88 | return new String[]{ 89 | "com.sonymobile.settings.SomcSettingsHeader", 90 | "com.sonymobile.settings.preference.util.SomcPreferenceActivity", 91 | "com.sonymobile.settings.preference.util.SomcSettingsPreferenceFragment" 92 | }; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/de/robv/android/xposed/XC_MethodHookRecursive.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import at.jclehner.appopsxposed.util.Util; 4 | 5 | public final class XC_MethodHookRecursive extends XC_MethodHook 6 | { 7 | private final XC_MethodHook mHook; 8 | private final Class mTargetClass; 9 | 10 | public XC_MethodHookRecursive(XC_MethodHook hook, Class targetClass) 11 | { 12 | mHook = hook; 13 | mTargetClass = targetClass; 14 | } 15 | 16 | public Class getTargetClass() { 17 | return mTargetClass; 18 | } 19 | 20 | @Override 21 | protected final void beforeHookedMethod(MethodHookParam param) throws Throwable 22 | { 23 | if(!isValidThisObject(param)) 24 | Util.debug("Skipping beforeHookedMethod with " + param.method.getDeclaringClass() + "#" + param.method.getName()); 25 | else 26 | mHook.beforeHookedMethod(param); 27 | } 28 | 29 | @Override 30 | protected final void afterHookedMethod(MethodHookParam param) throws Throwable 31 | { 32 | if(!isValidThisObject(param)) 33 | Util.debug("Skipping afterHookedMethod with " + param.method.getDeclaringClass() + "#" + param.method.getName()); 34 | else 35 | mHook.afterHookedMethod(param); 36 | } 37 | 38 | private boolean isValidThisObject(MethodHookParam param) { 39 | return param.thisObject == null || mTargetClass == null || mTargetClass.isInstance(param.thisObject); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/res/drawable-v19/ic_appops_sense.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/checkerboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/checkerboard.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/checkerboard_framed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/checkerboard_framed.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_black.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_cog_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_cog_black.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_cog_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_cog_circle.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_cog_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_cog_grey.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_cog_teal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_cog_teal.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_cog_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_cog_white.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_shield_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_shield_circle.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_shield_teal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_shield_teal.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_appops_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_appops_white.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_launcher2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable-xhdpi/ic_launcher2.png -------------------------------------------------------------------------------- /src/main/res/drawable/ic_appops_sense.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XspeedPL/AppOpsXposed/5f580e247e0b6e9c55f78311baea2cd2f4883510/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/layout/app_ops_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 24 | 25 | 32 | 33 | 34 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/res/layout/app_ops_details_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 33 | 34 | 43 | 44 | 58 | 59 | 70 | 71 | 81 | 89 | 90 | 91 | 99 | 100 | -------------------------------------------------------------------------------- /src/main/res/layout/app_ops_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 29 | 30 | 39 | 40 | 51 | 52 | 59 | 60 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/res/layout/app_ops_summary.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 24 | 25 | 31 | 32 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/res/layout/checkable_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/res/layout/icon_dropdown.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/res/layout/icon_spinner.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/res/layout/launcher_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 23 | 24 |