big changes

This commit is contained in:
Dr-TSNG 2021-04-10 22:38:34 +08:00
commit 7afdc58358
17 changed files with 241 additions and 152 deletions

View file

@ -6,6 +6,8 @@ At the same time, some "smart" apps use various loopholes to acquire your applis
This module provides some methods to test whether you have already hided your applist nicely.
Also, it can work as an Xposed module to hide some apps or reject applist requests to protect your privacy.
Attension: Xposed API version lower than 93 is NOT supported due to a permission problem
## Compilation
This project uses Hidden API, so you should replace android.jar in Android Studio. [Reference](https://github.com/anggrayudi/android-hidden-api)
## 关于该模块
虽然“检测安装的应用是不正确的做法”,而且很蠢,但是并不是所有的插件类应用都提供了随机包名支持。在这种情况下,检测到安装了root类应用(如Fake Location、存储重定向)与检测到了root本身区别不大。(会使用检测手段的app可不会认为你是在“我就蹭蹭不进去”)

View file

@ -18,13 +18,13 @@
android:supportsRtl="true"
android:theme="@style/Theme.HideMyApplist">
<activity
android:name=".ScopeManageActivity"
android:name=".ui.ScopeManageActivity"
android:label="@string/xposed_scope_manage"/>
<activity
android:name=".TemplateSettingsActivity"
android:name=".ui.TemplateSettingsActivity"
android:label="@string/xposed_edit_template" />
<activity
android:name=".TemplateManageActivity"
android:name=".ui.TemplateManageActivity"
android:label="@string/xposed_template_manage" />
<activity
android:name=".MainActivity"
@ -45,6 +45,9 @@
<meta-data
android:name="xposedminversion"
android:value="93" />
<meta-data
android:name="xposedscope"
android:resource="@array/xposed_scope" />
</application>
</manifest>

View file

@ -0,0 +1,38 @@
package com.tsng.hidemyapplist.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tsng.hidemyapplist.BuildConfig
import com.tsng.hidemyapplist.R
import mehdi.sakout.aboutpage.AboutPage
import mehdi.sakout.aboutpage.Element
class AboutFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return AboutPage(activity)
.setDescription(getString(R.string.about_my_description))
.addItem(Element().apply {
title = "Version " + BuildConfig.VERSION_NAME
iconDrawable = R.drawable.ic_baseline_language_24
})
.addItem(Element().apply {
title = getString(R.string.about_my_how_to_use_title)
iconDrawable = R.drawable.ic_baseline_help_24
setOnClickListener {
MaterialAlertDialogBuilder(requireContext())
.setTitle(title)
.setMessage(getString(R.string.about_my_how_to_use_message))
.setPositiveButton(getString(R.string.accept), null)
.show()
}
})
.addWebsite("http://www.coolapk.com/u/1911298", getString(R.string.about_my_author))
.addGitHub("Dr-TSNG/Hide-My-Applist", getString(R.string.about_my_project_address))
.addItem(Element())
.create()
}
}

View file

@ -0,0 +1,292 @@
package com.tsng.hidemyapplist.ui;
import android.Manifest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.tsng.hidemyapplist.R;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.IntFunction;
public class DetectionFragment extends Fragment implements View.OnClickListener {
View root;
Activity main;
Set<String> targets;
SharedPreferences default_pref;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_detection, container, false);
default_pref = PreferenceManager.getDefaultSharedPreferences(getContext());
main = getActivity();
ReadTargets();
UpdateTargetPackageView();
root.findViewById(R.id.detection_btn_AddPackage).setOnClickListener(this);
root.findViewById(R.id.detection_btn_StartDetect).setOnClickListener(this);
((ListView) root.findViewById(R.id.detection_lv_CurrentPackages)).setOnItemLongClickListener((parent, view, position, id) -> {
String s = ((TextView) view).getText().toString();
new MaterialAlertDialogBuilder(main)
.setTitle(getString(R.string.detection_delete_confirm))
.setMessage(s)
.setNegativeButton(getString(R.string.cancel), null)
.setPositiveButton(getString(R.string.accept), ((dialog, which) -> {
targets.remove(s);
SaveTargets();
UpdateTargetPackageView();
})).show();
return true;
});
return root;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.detection_btn_AddPackage:
EditText et = root.findViewById(R.id.detection_et_DetectionTarget);
String text = et.getText().toString();
if (!text.isEmpty())
targets.add(text);
SaveTargets();
UpdateTargetPackageView();
et.setText(null);
break;
case R.id.detection_btn_StartDetect:
DetectionFragment.DetectionTask task = new DetectionFragment.DetectionTask();
task.execute();
break;
}
}
private class DetectionTask extends AsyncTask<Void, Void, Void> {
int progress;
int[][] methodStatus = new int[5][30];
ProgressDialog dialog;
final Map<String, Integer> M0 = new LinkedHashMap<String, Integer>() {{
put("pm", 0);
put("getInstalledPackages", 1);
put("getInstalledApplications", 2);
put("getPackagesHoldingPermissions", 3);
put("queryInstrumentation", 4);
}};
final Map<String, Integer> M1 = new LinkedHashMap<String, Integer>() {{
put("queryIntentActivities", 0);
}};
final Map<String, Integer> M2 = new LinkedHashMap<String, Integer>() {{
put("getPackagesForUid", 0);
}};
final Map<String, Integer> M3 = new LinkedHashMap<String, Integer>() {{
put("javaFile", 0);
}};
@Override
protected void onPreExecute() {
progress = 1;
dialog = new ProgressDialog(main);
dialog.setCancelable(false);
dialog.setTitle(getResources().getString(R.string.detection_executing_detections));
dialog.setMessage(getResources().getString(R.string.detection_using_method) + " 1/5");
dialog.show();
}
@Override
protected void onProgressUpdate(Void... voids) {
dialog.setMessage(getResources().getString(R.string.detection_using_method) + " " + (++progress) + "/5");
}
@Override
protected Void doInBackground(Void... voids) {
method_pm();
publishProgress();
checkList(0, M0.get("getInstalledPackages"), main.getPackageManager().getInstalledPackages(0));
publishProgress();
checkList(0, M0.get("getInstalledApplications"), main.getPackageManager().getInstalledApplications(0));
publishProgress();
method_getPackagesHoldingPermissions();
publishProgress();
checkList(0, M0.get("queryInstrumentation"), main.getPackageManager().queryInstrumentation(null, PackageManager.GET_META_DATA));
publishProgress();
method_intent();
publishProgress();
method_uid();
publishProgress();
method_datafile();
publishProgress();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
dialog.dismiss();
IntFunction res = (int r) -> r == 1 ? "\uD83D\uDFE5" : r == 0 ? "\uD83D\uDFE9" : "\uD83D\uDFE8";
StringBuilder br = new StringBuilder();
br.append("API requests:\n");
for (Map.Entry<String, Integer> entry : M0.entrySet())
br.append(res.apply(methodStatus[0][entry.getValue()])).append(entry.getKey()).append('\n');
br.append("Intent queries:\n");
for (Map.Entry<String, Integer> entry : M1.entrySet())
br.append(res.apply(methodStatus[1][entry.getValue()])).append(entry.getKey()).append('\n');
br.append("UID detections:\n");
for (Map.Entry<String, Integer> entry : M2.entrySet())
br.append(res.apply(methodStatus[2][entry.getValue()])).append(entry.getKey()).append('\n');
br.append("File detections:\n");
for (Map.Entry<String, Integer> entry : M3.entrySet())
br.append(res.apply(methodStatus[3][entry.getValue()])).append(entry.getKey()).append('\n');
new MaterialAlertDialogBuilder(main)
.setTitle(getString(R.string.detection_finished))
.setMessage(br.toString())
.setPositiveButton(getString(R.string.accept), null).show();
}
private void checkList(int generalId, int methodId, List list) {
if (list == null) {
methodStatus[generalId][methodId] = -1;
return;
}
Set<String> packages = new TreeSet<>();
try {
for (Object it : list)
packages.add((String) it.getClass().getField("packageName").get(it));
} catch (Exception ignored) { }
methodStatus[generalId][methodId] = findPackages(packages);
}
private void method_pm() {
Set<String> packages = new TreeSet<>();
try {
java.lang.Process p = Runtime.getRuntime().exec("pm list packages");
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.length() > 8) {
String prefix = line.substring(0, 8);
if (prefix.equalsIgnoreCase("package:")) {
line = line.substring(8).trim();
if (!TextUtils.isEmpty(line))
packages.add(line);
}
}
}
br.close();
p.destroy();
} catch (Exception ignored) { }
if (packages.isEmpty()) packages = null;
methodStatus[0][M0.get("pm")] = findPackages(packages);
}
private void method_getPackagesHoldingPermissions() {
List<String> permissions = new ArrayList<>();
for (Field field : Manifest.permission.class.getFields()) {
try {
if (field.getType() == String.class)
permissions.add((String) field.get(null));
} catch (Exception ignored) { }
}
checkList(0, M0.get("getPackagesHoldingPermissions"), main.getPackageManager().getPackagesHoldingPermissions(permissions.toArray(new String[0]), 0));
}
private void method_intent() {
Set<String> packages = new TreeSet<>();
Intent intent = new Intent(Intent.ACTION_MAIN);
List<ResolveInfo> infos = main.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL);
for (ResolveInfo i : infos)
packages.add(i.activityInfo.packageName);
if (packages.isEmpty()) packages = null;
methodStatus[1][M1.get("queryIntentActivities")] = findPackages(packages);
}
private void method_uid() {
Set<String> packages = new TreeSet<>();
for (int i = Process.SYSTEM_UID; i <= Process.LAST_APPLICATION_UID; i++) {
String[] uid = main.getPackageManager().getPackagesForUid(i);
if (uid != null)
Collections.addAll(packages, uid);
}
methodStatus[2][M2.get("getPackagesForUid")] = findPackages(packages);
}
private void method_datafile() {
Set<String> packages = new TreeSet<>();
try {
for (String pkg : targets) {
File f = new File("/storage/emulated/0/Android/data/" + pkg);
if (f.exists()) packages.add(pkg);
}
} catch (Throwable t) {
Log.e("db_method_datafile", t.toString());
}
methodStatus[3][M3.get("javaFile")] = findPackages(packages);
}
private int findPackages(Set<String> packages) {
if (packages == null) return -1;
for (String name : packages)
if (targets.contains(name))
return 1;
return 0;
}
}
private void UpdateTargetPackageView() {
ListView lv = root.findViewById(R.id.detection_lv_CurrentPackages);
ArrayAdapter<String> adapter = new ArrayAdapter<>(main, android.R.layout.simple_list_item_1, targets.toArray(new String[0]));
lv.setAdapter(adapter);
}
private void SaveTargets() {
default_pref.edit().putStringSet("DetectionSet", targets).apply();
}
private void ReadTargets() {
targets = default_pref.getStringSet("DetectionSet", null);
if (targets == null)
targets = new TreeSet<>(Arrays.asList(getResources().getStringArray(R.array.packages)));
else
targets = new TreeSet<>(targets);
}
}

View file

@ -0,0 +1,91 @@
package com.tsng.hidemyapplist.ui
import android.content.pm.ApplicationInfo
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat
import com.tsng.hidemyapplist.R
import kotlin.collections.set
class ScopeManageActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scope_manage)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.xposed_score_container, SettingsFragment())
.commit()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount == 0)
Toast.makeText(this, getString(R.string.xposed_restart_to_apply), Toast.LENGTH_SHORT).show();
super.onBackPressed()
}
class SettingsFragment : PreferenceFragmentCompat() {
private lateinit var map: MutableMap<String, String>
private lateinit var templates: Set<String>
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = "Scope"
preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE
setPreferencesFromResource(R.xml.scope_preferences, rootKey)
map = preferenceManager.sharedPreferences.all as MutableMap<String, String>
templates = setOf("<close>") +
requireActivity().getSharedPreferences("Templates", MODE_WORLD_READABLE).getStringSet("List", setOf())!!
refresh()
}
private fun refresh() {
val packages = requireActivity().packageManager.getInstalledPackages(0)
packages.removeAll { it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0 }
packages.sortWith { o1, o2 ->
val l1 = o1.applicationInfo.loadLabel(requireActivity().packageManager) as String
val l2 = o2.applicationInfo.loadLabel(requireActivity().packageManager) as String
if (map.containsKey(o1.packageName) xor map.containsKey(o2.packageName))
if (map.containsKey(o2.packageName)) 1
else -1
else
l1.compareTo(l2)
}
preferenceScreen.removeAll()
for (pkg in packages)
preferenceScreen.addPreference(ListPreference(context).apply {
dialogTitle = getString(R.string.xposed_teplate_select)
title = pkg.applicationInfo.loadLabel(requireActivity().packageManager)
icon = pkg.applicationInfo.loadIcon(requireActivity().packageManager)
key = pkg.packageName
entries = templates.toTypedArray()
entryValues = templates.toTypedArray()
if (map.containsKey(pkg.packageName))
summary = map[pkg.packageName]
setOnPreferenceChangeListener { _, newValue ->
if (newValue.equals("<close>")) {
summary = null
map.remove(pkg.packageName)
value = "<close>"
preferenceManager.sharedPreferences.edit().remove(pkg.packageName).apply()
false
} else {
summary = newValue as String
map[pkg.packageName] = newValue
true
}
}
})
}
}
}

View file

@ -0,0 +1,92 @@
package com.tsng.hidemyapplist.ui;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.tsng.hidemyapplist.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class TemplateManageActivity extends AppCompatActivity {
Set<String> templates;
ArrayAdapter<String> adapter;
SharedPreferences list_pref;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_template_manage);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
InitTemplateList();
findViewById(R.id.xposed_btn_new_template).setOnClickListener(v -> {
final EditText ev = new EditText(this);
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.xposed_new_template))
.setView(ev)
.setNegativeButton(getString(R.string.cancel), null)
.setPositiveButton(getString(R.string.accept), ((dialog, which) -> {
String name = ev.getText().toString();
if (templates.contains(name) || name.isEmpty())
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.error))
.setMessage(getString(R.string.xposed_template_already_exists))
.setPositiveButton(getString(R.string.accept), null)
.show();
else {
templates.add(name);
adapter.add(name);
list_pref.edit().putStringSet("List", new HashSet<>(templates)).apply();
EditTemplate(name);
}
})).setCancelable(false).show();
});
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
private void InitTemplateList() {
list_pref = getSharedPreferences("Templates", Context.MODE_WORLD_READABLE);
templates = new HashSet<>(list_pref.getStringSet("List", new HashSet<>()));
ListView lv = findViewById(R.id.xposed_lv_template);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>(templates));
lv.setAdapter(adapter);
lv.setOnItemClickListener((parent, view, position, id) -> EditTemplate(((TextView) view).getText().toString()));
lv.setOnItemLongClickListener((parent, view, position, id) -> {
String s = ((TextView) view).getText().toString();
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.xposed_template_delete_confirm))
.setMessage(s)
.setNegativeButton(getString(R.string.cancel), null)
.setPositiveButton(getString(R.string.accept), ((dialog, which) -> {
templates.remove(s);
adapter.remove(s);
getSharedPreferences("tpl_" + s, MODE_WORLD_READABLE).edit().clear().apply();
list_pref.edit().putStringSet("List", new HashSet<>(templates)).apply();
})).show();
return true;
});
}
private void EditTemplate(String name) {
Intent intent = new Intent(this, TemplateSettingsActivity.class);
intent.putExtra("template", name);
startActivity(intent);
}
}

View file

@ -0,0 +1,103 @@
package com.tsng.hidemyapplist.ui
import android.content.pm.ApplicationInfo
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.CheckBoxPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.tsng.hidemyapplist.R
class TemplateSettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
private var template: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_template_settings)
template = intent.getStringExtra("template")
supportActionBar?.setDisplayHomeAsUpEnabled(true)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.xposed_template_container, SettingsFragment().apply {
arguments = Bundle().apply { putString("template", template) }
})
.commit()
}
}
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean {
val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)
fragment.arguments = Bundle().apply { putString("template", template) }
supportFragmentManager
.beginTransaction()
.replace(R.id.xposed_template_container, fragment)
.addToBackStack(null)
.commit()
return true
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount == 0)
Toast.makeText(this, getString(R.string.xposed_restart_to_apply), Toast.LENGTH_SHORT).show();
super.onBackPressed()
}
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = "tpl_" + arguments?.getString("template")
preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE
setPreferencesFromResource(R.xml.template_preferences, rootKey)
}
}
class HideAppsFragment : PreferenceFragmentCompat() {
private lateinit var list: MutableSet<String>
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = "tpl_" + arguments?.getString("template")
preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE
setPreferencesFromResource(R.xml.template_hideapps_preference, rootKey)
list = preferenceManager.sharedPreferences.getStringSet("HideApps", setOf())!!.toMutableSet()
refresh()
}
private fun refresh() {
val packages = requireActivity().packageManager.getInstalledPackages(0)
packages.removeAll { it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0 }
packages.sortWith { o1, o2 ->
val l1 = o1.applicationInfo.loadLabel(requireActivity().packageManager) as String
val l2 = o2.applicationInfo.loadLabel(requireActivity().packageManager) as String
if (list.contains(o1.packageName) xor list.contains(o2.packageName))
if (list.contains(o2.packageName)) 1
else -1
else
l1.compareTo(l2)
}
preferenceScreen.removeAll()
for (pkg in packages)
preferenceScreen.addPreference(CheckBoxPreference(context).apply {
title = pkg.applicationInfo.loadLabel(requireActivity().packageManager)
icon = pkg.applicationInfo.loadIcon(requireActivity().packageManager)
isChecked = list.contains(pkg.packageName)
setOnPreferenceChangeListener { _, newValue ->
if (newValue == true)
list.add(pkg.packageName)
else
list.remove(pkg.packageName)
preferenceManager.sharedPreferences.edit().putStringSet("HideApps", list.toSet()).apply()
true
}
})
}
}
}

View file

@ -0,0 +1,69 @@
package com.tsng.hidemyapplist.ui;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.tsng.hidemyapplist.R;
public class XposedFragment extends Fragment implements View.OnClickListener {
View root;
Activity main;
boolean isHookSelf, isHookSelfOnCreate;
private boolean getXposedStatus() {
return false;
}
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_xposed, container, false);
main = getActivity();
root.findViewById(getXposedStatus() ? R.id.xposed_activated : R.id.xposed_not_activated).setVisibility(View.VISIBLE);
Button btn_HookSelf = root.findViewById(R.id.hook_self);
isHookSelf = isHookSelfOnCreate = getActivity().getSharedPreferences("Templates", Context.MODE_WORLD_READABLE).getBoolean("HookSelf", false);
btn_HookSelf.setBackgroundColor(isHookSelf ? ContextCompat.getColor(getContext(), R.color.cyan_A700) : Color.GRAY);
btn_HookSelf.setOnClickListener(this);
root.findViewById(R.id.xposed_tv_template_manage).setOnClickListener(this);
root.findViewById(R.id.xposed_tv_scope_manage).setOnClickListener(this);
return root;
}
@Override
public void onClick(View v) {
if(!getXposedStatus())
Toast.makeText(getContext(), getString(R.string.xposed_actvate_first), Toast.LENGTH_SHORT).show();
else switch (v.getId()) {
case R.id.hook_self:
isHookSelf = !isHookSelf;
getActivity().getSharedPreferences("Templates", Context.MODE_WORLD_READABLE).edit().putBoolean("HookSelf", isHookSelf).apply();
v.setBackgroundColor(isHookSelf ? ContextCompat.getColor(getContext(), R.color.cyan_A700) : Color.GRAY);
Toast.makeText(getContext(), getString(R.string.xposed_restart_self_to_apply), Toast.LENGTH_SHORT).show();
break;
case R.id.xposed_tv_template_manage:
if (isHookSelfOnCreate)
Toast.makeText(getContext(), getString(R.string.xposed_disable_hook_self_first), Toast.LENGTH_SHORT).show();
else
startActivity(new Intent(main, TemplateManageActivity.class));
break;
case R.id.xposed_tv_scope_manage:
if (isHookSelfOnCreate)
Toast.makeText(getContext(), getString(R.string.xposed_disable_hook_self_first), Toast.LENGTH_SHORT).show();
else
startActivity(new Intent(main, ScopeManageActivity.class));
break;
}
}
}

View file

@ -1,18 +0,0 @@
package com.tsng.hidemyapplist.xposed;
import android.util.Log;
import com.tsng.hidemyapplist.BuildConfig;
import com.tsng.hidemyapplist.xposed.hooks.IndividualHooks;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
public class XposedEntry implements IXposedHookLoadPackage {
public static final String LOG = "hma_log";
public static final String APPNAME = BuildConfig.APPLICATION_ID;
@Override
public void handleLoadPackage(final LoadPackageParam lpp) {
new IndividualHooks().handleLoadPackage(lpp);
}
}

View file

@ -0,0 +1,58 @@
package com.tsng.hidemyapplist.xposed
import android.util.Log
import com.tsng.hidemyapplist.BuildConfig
import com.tsng.hidemyapplist.xposed.hooks.PackageManagerService
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
class XposedEntry : IXposedHookLoadPackage {
override fun handleLoadPackage(lpp: LoadPackageParam) {
if (lpp.packageName == APPNAME) {
XposedHelpers.findAndHookMethod("com.tsng.hidemyapplist.ui.XposedFragment", lpp.classLoader, "getXposedStatus", object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
param.result = true
}
})
}
PackageManagerService().handleLoadPackage(lpp)
//IndividualHooks().handleLoadPackage(lpp)
}
companion object {
const val LOG = "hma_log"
const val APPNAME = BuildConfig.APPLICATION_ID
@JvmStatic
fun getTemplatePref(pkg: String?): XSharedPreferences? {
val template = XSharedPreferences(APPNAME, "Scope").getString(pkg, null) ?: return null
return XSharedPreferences(APPNAME, "tpl_$template")
}
@JvmStatic
fun isUseHook(pref: XSharedPreferences, callerName: String, hook: String): Boolean {
if (callerName == APPNAME)
if (!XSharedPreferences(APPNAME, "Templates").getBoolean("HookSelf", false))
return false
val enableAllHooks = pref.getBoolean("EnableAllHooks", false)
val enabled = pref.getStringSet("ApplyHooks", HashSet())
return enableAllHooks or enabled.contains(hook)
}
@JvmStatic
fun isToHide(pref: XSharedPreferences?, callerName: String, pkgstr: String): Boolean {
if (pref == null) return false
if (pref.getBoolean("ExcludeSelf", false) && pkgstr.contains(callerName)) return false
if (pref.getBoolean("HideAllApps", false)) return true
val set = pref.getStringSet("HideApps", HashSet())
for (pkg in set) if (pkgstr.contains(pkg)) {
Log.d(LOG, "HIDE $pkgstr")
return true
}
return false
}
}
}

View file

@ -11,7 +11,6 @@ import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@ -22,50 +21,24 @@ import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import static com.tsng.hidemyapplist.xposed.XposedEntry.LOG;
import static com.tsng.hidemyapplist.xposed.XposedEntry.APPNAME;
import static com.tsng.hidemyapplist.xposed.XposedEntry.LOG;
import static com.tsng.hidemyapplist.xposed.XposedEntry.getTemplatePref;
import static com.tsng.hidemyapplist.xposed.XposedEntry.isToHide;
public class IndividualHooks implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(final LoadPackageParam lpp) {
if (lpp.packageName.equals(APPNAME)) {
XposedHelpers.findAndHookMethod("com.tsng.hidemyapplist.ui.xposed.XposedFragment", lpp.classLoader, "getXposedStatus", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
param.setResult(true);
}
});
if (!new XSharedPreferences(APPNAME, APPNAME + "_preferences").getBoolean("HookSelf", false))
return;
}
//判断模块是否生效
XSharedPreferences pref = new XSharedPreferences(APPNAME, "Scope");
final String template = pref.getString(lpp.packageName, null);
if (template == null) return;
//判断是否hook该应用
pref = new XSharedPreferences(APPNAME, "tpl_" + template);
final XSharedPreferences pref = getTemplatePref(lpp.packageName);
if (pref == null) return;
final boolean enable_all_hooks = pref.getBoolean("EnableAllHooks", false);
final Set<String> enabled = pref.getStringSet("ApplyHooks", null);
//获取模板
if (enable_all_hooks || enabled.contains("method_pm")) pmHook(lpp, pref);
if (enable_all_hooks || enabled.contains("method_api")) apiHook(lpp, pref);
if (enable_all_hooks || enabled.contains("method_intent")) intentHook(lpp, pref);
if (enable_all_hooks || enabled.contains("method_uid")) uidHook(lpp, pref);
if (enable_all_hooks || enabled.contains("method_datafile")) fileHook(lpp, pref);
//下钩子
}
boolean isToHide(final XSharedPreferences pref, String lppname, String pkgstr) {
if (pref.getBoolean("ExcludeSelf", false) && pkgstr.contains(lppname)) return false;
if (pref.getBoolean("HideAllApps", false)) return true;
Set<String> set = pref.getStringSet("HideApps", new HashSet<>());
for (String pkg : set)
if (pkgstr.contains(pkg))
return true;
return false;
}
void pmHook(final LoadPackageParam lpp, final XSharedPreferences pref) {

View file

@ -1,26 +1,56 @@
package com.tsng.hidemyapplist.xposed.hooks;
import android.content.pm.PackageInfo;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.util.Log;
import java.lang.reflect.Method;
import java.util.Iterator;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import com.tsng.hidemyapplist.xposed.XposedEntry;
import static com.tsng.hidemyapplist.xposed.XposedEntry.APPNAME;
import static com.tsng.hidemyapplist.xposed.XposedEntry.LOG;
import static com.tsng.hidemyapplist.xposed.XposedEntry.getTemplatePref;
import static com.tsng.hidemyapplist.xposed.XposedEntry.isToHide;
import static com.tsng.hidemyapplist.xposed.XposedEntry.isUseHook;
public class PackageManagerService implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(LoadPackageParam lpp) {
if(!lpp.packageName.equals("android"))return;
Class PMS = XposedHelpers.findClass("com.android.server.pm.PackageManagerService", lpp.classLoader);
XC_MethodHook apiHook = new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param){
String callerName = (String) XposedHelpers.callMethod(param.thisObject, "getNameForUid", Binder.getCallingUid());
Log.d(XposedEntry.LOG, "caller: " + callerName);
if (!lpp.packageName.equals("android")) return;
Class PKMS = XposedHelpers.findClass("com.android.server.pm.PackageManagerService", lpp.classLoader);
for (Method method : PKMS.getDeclaredMethods())
switch (method.getName()) {
case "getInstalledPackages":
case "getInstalledApplications":
case "getPackagesHoldingPermissions":
case "queryInstrumentation":
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String callerName = (String) XposedHelpers.callMethod(param.thisObject, "getNameForUid", Binder.getCallingUid());
if (callerName == APPNAME) {
Log.d(LOG, "PKMS caller: " + callerName);
Log.d(LOG, "PKMS method: " + param.method.getName());
}
XSharedPreferences pref = getTemplatePref(callerName);
if (!isUseHook(pref, callerName, "method_api")) return;
for (Iterator iterator = ((ParceledListSlice)param.getResult()).getList().iterator(); iterator.hasNext(); )
if (isToHide(pref, callerName, (String) XposedHelpers.getObjectField(iterator.next(), "packageName")))
iterator.remove();
Log.d(LOG, "PKMS dealt");
}
});
break;
}
};
}
}

View file

@ -6,18 +6,18 @@
<fragment
android:id="@+id/navigation_detection"
android:name="com.tsng.hidemyapplist.ui.detection.DetectionFragment"
android:name="com.tsng.hidemyapplist.ui.DetectionFragment"
android:label="@string/title_detection"
tools:layout="@layout/fragment_detection" />
<fragment
android:id="@+id/navigation_xposed"
android:name="com.tsng.hidemyapplist.ui.xposed.XposedFragment"
android:name="com.tsng.hidemyapplist.ui.XposedFragment"
android:label="@string/title_xposed"
tools:layout="@layout/fragment_xposed" />
<fragment
android:id="@+id/navigation_about"
android:name="com.tsng.hidemyapplist.ui.about.AboutFragment"
android:name="com.tsng.hidemyapplist.ui.AboutFragment"
android:label="@string/title_about"/>
</navigation>

View file

@ -3,10 +3,6 @@
<string name="detection_hint_input_name">输入要检测的包名</string>
<string name="detection_add_new_package">添加</string>
<string name="detection_start">开始检测</string>
<string name="detection_method">检测方法</string>
<string name="detection_target_found">找到包名</string>
<string name="detection_target_not_found">未找到包名</string>
<string name="detection_permission_denied">拒绝访问</string>
<string name="detection_executing_detections">检测中</string>
<string name="detection_finished">检测结束</string>
<string name="detection_using_method">执行检测方法</string>

View file

@ -1,4 +1,7 @@
<resources>
<string-array name="xposed_scope">
<item>android</item>
</string-array>
<string-array name="detect_methods_entries">
<item>pm command</item>
<item>API request</item>

View file

@ -3,10 +3,6 @@
<string name="detection_hint_input_name">Input package name</string>
<string name="detection_add_new_package">add</string>
<string name="detection_start">start detection</string>
<string name="detection_method">Detection method</string>
<string name="detection_target_found">Target found</string>
<string name="detection_target_not_found">Target not found.</string>
<string name="detection_permission_denied">Permission denied.</string>
<string name="detection_executing_detections">Executing detections</string>
<string name="detection_finished">Detection finished</string>
<string name="detection_using_method">Using method</string>
@ -15,7 +11,7 @@
<item>com.topjohnwu.magisk</item>
<item>de.robv.android.xposed.installer</item>
<item>org.meowcat.edxposed.manager</item>
<item>io.github.lsposed.manager</item>
<item>org.lsposed.manager</item>
<item>me.weishu.exp</item>
</string-array>
</resources>

View file

@ -28,7 +28,7 @@
android:title="@string/xposed_template_exclude_self" />
<Preference
android:dependency="HideAllApps"
android:fragment="com.tsng.hidemyapplist.TemplateSettingsActivity$HideAppsFragment"
android:fragment="com.tsng.hidemyapplist.ui.TemplateSettingsActivity$HideAppsFragment"
android:title="@string/xposed_template_choose_apps_to_hide" />