diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6088baa1..b6b4bcb6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,9 @@ + finish()); + ActionBar bar = getSupportActionBar(); + if (bar != null) { + bar.setDisplayHomeAsUpEnabled(true); + bar.setSubtitle(moduleName); + } + setupWindowInsets(binding.snackbar, binding.recyclerView); + appAdapter = new ScopeAdapter(this, modulePackageName); + appAdapter.setHasStableIds(true); + binding.recyclerView.setAdapter(appAdapter); + binding.recyclerView.setLayoutManager(new LinearLayoutManager(this)); + if (!XposedApp.getPreferences().getBoolean("md2", false)) { + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, + DividerItemDecoration.VERTICAL); + binding.recyclerView.addItemDecoration(dividerItemDecoration); + } + appAdapter.setCallback(this); + handler.postDelayed(runnable, 300); + binding.swipeRefreshLayout.setOnRefreshListener(() -> appAdapter.refresh()); + + searchListener = new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + appAdapter.filter(query); + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + appAdapter.filter(newText); + return false; + } + }; + } + + @Override + public boolean onCreateOptionsMenu(@NonNull Menu menu) { + getMenuInflater().inflate(R.menu.menu_app_list, menu); + searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); + searchView.setOnQueryTextListener(searchListener); + return super.onCreateOptionsMenu(menu); + } + + @Override + public void onDataReady() { + handler.removeCallbacks(runnable); + binding.swipeRefreshLayout.setRefreshing(false); + String queryStr = searchView != null ? searchView.getQuery().toString() : ""; + runOnUiThread(() -> appAdapter.getFilter().filter(queryStr)); + } + + @Override + public void onItemClick(View v, ApplicationInfo info) { + AppHelper.showMenu(this, getSupportFragmentManager(), v, info); + } + + @Override + public void onBackPressed() { + if (searchView.isIconified()) { + super.onBackPressed(); + } else { + searchView.setIconified(true); + } + } +} diff --git a/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java index b1292dde..4d982a3e 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java @@ -69,10 +69,10 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi private SearchView.OnQueryTextListener mSearchListener; private PackageManager pm; private Comparator cmp; - private DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + private final DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); private ModuleUtil moduleUtil; private ModuleAdapter adapter = null; - private Runnable reloadModules = new Runnable() { + private final Runnable reloadModules = new Runnable() { public void run() { String queryStr = searchView != null ? searchView.getQuery().toString() : ""; ArrayList showList; @@ -443,6 +443,12 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi case R.id.menu_uninstall: startActivity(new Intent(Intent.ACTION_UNINSTALL_PACKAGE, Uri.fromParts("package", module.packageName, null))); return true; + case R.id.menu_scope: + Intent scopeIntent = new Intent(this, ModuleScopeActivity.class); + scopeIntent.putExtra("modulePackageName", module.packageName); + scopeIntent.putExtra("moduleName", module.getAppName()); + startActivity(scopeIntent); + return true; } return super.onContextItemSelected(item); } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppAdapter.java b/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppAdapter.java index 5cd4168a..bdc7a742 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppAdapter.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppAdapter.java @@ -33,14 +33,14 @@ import java.util.Locale; public class AppAdapter extends RecyclerView.Adapter implements Filterable { - protected final Context context; + protected Context context; private final ApplicationInfo.DisplayNameComparator displayNameComparator; private Callback callback; - private List fullList, showList; - private DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + protected List fullList, showList; + private final DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); private List checkedList; - private PackageManager pm; - private ApplicationFilter filter; + private final PackageManager pm; + private final ApplicationFilter filter; private Comparator cmp; AppAdapter(Context context) { @@ -66,17 +66,36 @@ public class AppAdapter extends RecyclerView.Adapter impl } private void loadApps() { + boolean onlyInAppList = this instanceof ScopeAdapter; fullList = pm.getInstalledApplications(PackageManager.GET_META_DATA); - if (!XposedApp.getPreferences().getBoolean("show_modules", true)) { - List rmList = new ArrayList<>(); + List rmList = new ArrayList<>(); + if (!XposedApp.getPreferences().getBoolean("show_modules", true) || onlyInAppList) { for (ApplicationInfo info : fullList) { if (info.metaData != null && info.metaData.containsKey("xposedmodule") || AppHelper.FORCE_WHITE_LIST_MODULE.contains(info.packageName)) { rmList.add(info); } } - if (rmList.size() > 0) { - fullList.removeAll(rmList); + } + if (onlyInAppList && AppHelper.isBlackListMode()) { + if (AppHelper.isWhiteListMode()) { + List whiteList = AppHelper.getWhiteList(); + for (ApplicationInfo info : fullList) { + if (!whiteList.contains(info.packageName)) { + rmList.add(info); + } + } + } else { + List blackList = AppHelper.getBlackList(); + for (ApplicationInfo info : fullList) { + if (blackList.contains(info.packageName)) { + rmList.add(info); + } + } } + + } + if (rmList.size() > 0) { + fullList.removeAll(rmList); } AppHelper.makeSurePath(); checkedList = generateCheckedList(); @@ -152,7 +171,7 @@ public class AppAdapter extends RecyclerView.Adapter impl cmp = displayNameComparator; break; } - Collections.sort(fullList, (a, b) -> { + fullList.sort((a, b) -> { boolean aChecked = checkedList.contains(a.packageName); boolean bChecked = checkedList.contains(b.packageName); if (aChecked == bChecked) { diff --git a/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppHelper.java b/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppHelper.java index 9d2bd43c..24093bb6 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppHelper.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/adapters/AppHelper.java @@ -21,13 +21,18 @@ import org.meowcat.edxposed.manager.R; import org.meowcat.edxposed.manager.XposedApp; import org.meowcat.edxposed.manager.util.CompileUtil; +import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Objects; @@ -42,12 +47,15 @@ public class AppHelper { private static final String WHITE_LIST_PATH = "conf/whitelist/"; private static final String BLACK_LIST_PATH = "conf/blacklist/"; private static final String COMPAT_LIST_PATH = "conf/compatlist/"; + private static final String SCOPE_LIST_PATH = "conf/%s.conf"; private static final String WHITE_LIST_MODE = "conf/usewhitelist"; private static final String BLACK_LIST_MODE = "conf/blackwhitelist"; private static final List FORCE_WHITE_LIST = new ArrayList<>(XposedApp.isEnhancementEnabled() ? Arrays.asList(BuildConfig.APPLICATION_ID, "android") : Collections.singletonList(BuildConfig.APPLICATION_ID)); public static List FORCE_WHITE_LIST_MODULE = new ArrayList<>(FORCE_WHITE_LIST); + private static final HashMap> scopeList = new HashMap<>(); + @SuppressWarnings("OctalInteger") static void makeSurePath() { XposedApp.mkdirAndChmod(WHITE_LIST_PATH, 00777); @@ -331,4 +339,39 @@ public class AppHelper { static boolean removeCompatList(String packageName) { return compatListFileName(packageName, false); } + + static List getScopeList(String modulePackageName) { + if (scopeList.containsKey(modulePackageName)) { + return scopeList.get(modulePackageName); + } + File file = new File(BASE_PATH + String.format(SCOPE_LIST_PATH, modulePackageName)); + List s = new ArrayList<>(); + try { + BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); + for (String line; (line = bufferedReader.readLine()) != null; ) { + s.add(line); + } + scopeList.put(modulePackageName, s); + } catch (IOException e) { + e.printStackTrace(); + } + return s; + } + + static boolean saveScopeList(String modulePackageName, List list) { + File file = new File(BASE_PATH + String.format(SCOPE_LIST_PATH, modulePackageName)); + try { + PrintWriter pr = new PrintWriter(new FileWriter(file)); + for (String line : list) { + pr.println(line); + } + pr.close(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + scopeList.put(modulePackageName, list); + setFilePermissionsFromMode(file.getPath(), Context.MODE_WORLD_READABLE); + return true; + } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/org/meowcat/edxposed/manager/adapters/ScopeAdapter.java new file mode 100644 index 00000000..a868d883 --- /dev/null +++ b/app/src/main/java/org/meowcat/edxposed/manager/adapters/ScopeAdapter.java @@ -0,0 +1,53 @@ +package org.meowcat.edxposed.manager.adapters; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.widget.CompoundButton; + +import org.meowcat.edxposed.manager.R; +import org.meowcat.edxposed.manager.util.ToastUtil; + +import java.util.ArrayList; +import java.util.List; + +public class ScopeAdapter extends AppAdapter { + + private final String modulePackageName; + private List checkedList; + + public ScopeAdapter(Context context, String modulePackageName) { + super(context); + this.modulePackageName = modulePackageName; + } + + @Override + public List generateCheckedList() { + AppHelper.makeSurePath(); + List scopeList = AppHelper.getScopeList(modulePackageName); + List list = new ArrayList<>(); + for (ApplicationInfo info : fullList) { + list.add(info.packageName); + } + scopeList.retainAll(list); + checkedList = scopeList; + return checkedList; + } + + @Override + protected void onCheckedChange(CompoundButton view, boolean isChecked, ApplicationInfo info) { + if (isChecked) { + checkedList.add(info.packageName); + } else { + checkedList.remove(info.packageName); + } + if (!AppHelper.saveScopeList(modulePackageName, checkedList)) { + ToastUtil.showShortToast(context, R.string.add_package_failed); + if (!isChecked) { + checkedList.add(info.packageName); + } else { + checkedList.remove(info.packageName); + } + view.setChecked(!isChecked); + } + } +} diff --git a/app/src/main/res/menu/context_menu_modules.xml b/app/src/main/res/menu/context_menu_modules.xml index 35c87796..6444518e 100644 --- a/app/src/main/res/menu/context_menu_modules.xml +++ b/app/src/main/res/menu/context_menu_modules.xml @@ -4,6 +4,9 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0d95b6ae..4a3843d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -341,4 +341,5 @@ Prevent the software from detecting EdXposed Manager\nWARNING: Modules may not be able to start the EdXposed Manager UI properly, some fratures of EdXposed Manager in the system may not work properly Disable hidden API restrictions bypass Disable hidden API restrictions bypass will pass some detection (eg Snapchat), these APIs are disabled by default\nWARNING: Enable this option may cause some features do not work properly, or some other problems + Scope