big changes
This commit is contained in:
parent
2fec1c73d9
commit
7afdc58358
17 changed files with 241 additions and 152 deletions
|
|
@ -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可不会认为你是在“我就蹭蹭不进去”)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
38
app/src/main/java/com/tsng/hidemyapplist/ui/AboutFragment.kt
Normal file
38
app/src/main/java/com/tsng/hidemyapplist/ui/AboutFragment.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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" />
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue