From 7be4e66b989978d4f75a3bac27a43c8a059738c5 Mon Sep 17 00:00:00 2001 From: tehcneko <7764726+tehcneko@users.noreply.github.com> Date: Sat, 30 Jan 2021 21:56:15 +0800 Subject: [PATCH] Add support for recommended scope --- .../manager/adapters/ScopeAdapter.java | 78 ++++++++++++++++++- .../manager/ui/activity/AppListActivity.java | 19 +---- .../manager/ui/activity/SettingsActivity.java | 1 - .../lsposed/manager/util/ModuleUtil.java | 17 ++++ app/src/main/res/menu/menu_app_list.xml | 4 + app/src/main/res/values-zh-rCN/strings.xml | 5 ++ app/src/main/res/values/strings.xml | 5 ++ gradle.properties | 20 ++++- 8 files changed, 125 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java index 2113a3c5..f0bde248 100644 --- a/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java +++ b/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java @@ -22,6 +22,7 @@ import android.widget.Filter; import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -30,6 +31,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import com.google.android.material.checkbox.MaterialCheckBox; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import java.util.ArrayList; @@ -54,14 +56,17 @@ public class ScopeAdapter extends RecyclerView.Adapter private final ApplicationFilter filter; private final SharedPreferences preferences; private final String modulePackageName; + private final String moduleName; private final MasterSwitch masterSwitch; private List fullList, showList; private List checkedList; + private final List recommendedList; private boolean enabled = true; private ApplicationInfo selectedInfo; - public ScopeAdapter(AppListActivity activity, String modulePackageName, MasterSwitch masterSwitch) { + public ScopeAdapter(AppListActivity activity, String moduleName, String modulePackageName, MasterSwitch masterSwitch) { this.activity = activity; + this.moduleName = moduleName; this.modulePackageName = modulePackageName; this.masterSwitch = masterSwitch; preferences = App.getPreferences(); @@ -77,6 +82,8 @@ public class ScopeAdapter extends RecyclerView.Adapter notifyDataSetChanged(); } }); + ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName); + recommendedList = module.getScopeList(); enabled = ModuleUtil.getInstance().isModuleEnabled(modulePackageName); refresh(); } @@ -91,12 +98,15 @@ public class ScopeAdapter extends RecyclerView.Adapter private void loadApps() { activity.runOnUiThread(() -> masterSwitch.setChecked(enabled)); checkedList = AppHelper.getScopeList(modulePackageName); + if (checkedList.isEmpty() && hasRecommended()) { + checkRecommended(); + } fullList = pm.getInstalledPackages(PackageManager.GET_META_DATA); List installedList = new ArrayList<>(); List rmList = new ArrayList<>(); for (PackageInfo info : fullList) { installedList.add(info.packageName); - if (info.packageName.equals(((ScopeAdapter) this).modulePackageName)) { + if (info.packageName.equals(this.modulePackageName)) { rmList.add(info); continue; } @@ -153,9 +163,32 @@ public class ScopeAdapter extends RecyclerView.Adapter }); } + private void checkRecommended() { + checkedList.clear(); + checkedList.addAll(recommendedList); + AppHelper.saveScopeList(modulePackageName, checkedList); + notifyDataSetChanged(); + } + + private boolean hasRecommended() { + return recommendedList != null && !recommendedList.isEmpty(); + } + public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); - if (itemId == R.id.item_show_system) { + if (itemId == R.id.use_recommended) { + if (!checkedList.isEmpty()) { + new MaterialAlertDialogBuilder(activity) + .setTitle(R.string.use_recommended) + .setMessage(R.string.use_recommended_message) + .setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended()) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } else { + checkRecommended(); + } + return true; + } else if (itemId == R.id.item_show_system) { item.setChecked(!item.isChecked()); preferences.edit().putBoolean("show_system_apps", item.isChecked()).apply(); } else if (itemId == R.id.item_show_games) { @@ -227,6 +260,9 @@ public class ScopeAdapter extends RecyclerView.Adapter if (intent == null) { menu.removeItem(R.id.menu_launch); } + if (!hasRecommended()) { + menu.removeItem(R.id.use_recommended); + } menu.findItem(R.id.item_show_system).setChecked(preferences.getBoolean("show_system_apps", false)); menu.findItem(R.id.item_show_games).setChecked(preferences.getBoolean("show_games", false)); menu.findItem(R.id.item_show_modules).setChecked(preferences.getBoolean("show_modules", false)); @@ -276,7 +312,11 @@ public class ScopeAdapter extends RecyclerView.Adapter } }); - holder.appDescription.setText(activity.getString(R.string.app_description, info.packageName, info.versionName)); + String description = activity.getString(R.string.app_description, info.packageName, info.versionName); + if (hasRecommended() && recommendedList.contains(info.packageName)) { + description += "\n" + activity.getString(R.string.requested_by_module); + } + holder.appDescription.setText(description); holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> { activity.getMenuInflater().inflate(R.menu.menu_app_item, menu); @@ -391,6 +431,36 @@ public class ScopeAdapter extends RecyclerView.Adapter } } + public boolean onBackPressed() { + if (masterSwitch.isChecked() && checkedList.isEmpty()) { + if (hasRecommended()) { + new MaterialAlertDialogBuilder(activity) + .setTitle(R.string.use_recommended) + .setMessage(R.string.no_scope_selected_has_recommended) + .setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended()) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> { + ModuleUtil.getInstance().setModuleEnabled(modulePackageName, false); + Toast.makeText(activity, activity.getString(R.string.module_disabled_no_selection, moduleName), Toast.LENGTH_LONG).show(); + activity.finish(); + }) + .show(); + } else { + new MaterialAlertDialogBuilder(activity) + .setMessage(R.string.no_scope_selected) + .setPositiveButton(android.R.string.cancel, null) + .setNegativeButton(android.R.string.ok, (dialog, which) -> { + ModuleUtil.getInstance().setModuleEnabled(modulePackageName, false); + Toast.makeText(activity, activity.getString(R.string.module_disabled_no_selection, moduleName), Toast.LENGTH_LONG).show(); + activity.finish(); + }) + .show(); + } + return false; + } else { + return true; + } + } + public static String getAppLabel(ApplicationInfo info, PackageManager pm) { return info.loadLabel(pm).toString(); } diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java index 342f95f4..e036fabf 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java @@ -14,15 +14,12 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.SearchView; import androidx.recyclerview.widget.DividerItemDecoration; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import io.github.lsposed.manager.R; -import io.github.lsposed.manager.adapters.AppHelper; import io.github.lsposed.manager.adapters.ScopeAdapter; import io.github.lsposed.manager.databinding.ActivityAppListBinding; import io.github.lsposed.manager.util.LinearLayoutManagerFix; -import io.github.lsposed.manager.util.ModuleUtil; import me.zhanghai.android.fastscroll.FastScrollerBuilder; public class AppListActivity extends BaseActivity { @@ -38,12 +35,11 @@ public class AppListActivity extends BaseActivity { } }; private final Handler handler = new Handler(Looper.getMainLooper()); - private String modulePackageName; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - modulePackageName = getIntent().getStringExtra("modulePackageName"); + String modulePackageName = getIntent().getStringExtra("modulePackageName"); String moduleName = getIntent().getStringExtra("moduleName"); binding = ActivityAppListBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -54,7 +50,7 @@ public class AppListActivity extends BaseActivity { bar.setDisplayHomeAsUpEnabled(true); bar.setTitle(moduleName); bar.setSubtitle(modulePackageName); - scopeAdapter = new ScopeAdapter(this, modulePackageName, binding.masterSwitch); + scopeAdapter = new ScopeAdapter(this, moduleName, modulePackageName, binding.masterSwitch); scopeAdapter.setHasStableIds(true); binding.recyclerView.setAdapter(scopeAdapter); binding.recyclerView.setLayoutManager(new LinearLayoutManagerFix(this)); @@ -119,16 +115,7 @@ public class AppListActivity extends BaseActivity { @Override public void onBackPressed() { if (searchView.isIconified()) { - if (binding.masterSwitch.isChecked() && AppHelper.getScopeList(modulePackageName).isEmpty()) { - new MaterialAlertDialogBuilder(this) - .setMessage(R.string.no_scope_selected) - .setPositiveButton(android.R.string.cancel, null) - .setNegativeButton(android.R.string.ok, (dialog, which) -> { - ModuleUtil.getInstance().setModuleEnabled(modulePackageName, false); - super.onBackPressed(); - }) - .show(); - } else { + if (scopeAdapter.onBackPressed()) { super.onBackPressed(); } } else { diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java index 3c1c14ef..a66d9e66 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java @@ -3,7 +3,6 @@ package io.github.lsposed.manager.ui.activity; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.view.KeyEvent; import android.view.MotionEvent; diff --git a/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java b/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java index 951347d0..97322ed4 100644 --- a/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java +++ b/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java @@ -16,6 +16,7 @@ import com.google.android.material.snackbar.Snackbar; import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -24,6 +25,7 @@ import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import io.github.lsposed.manager.App; +import io.github.lsposed.manager.BuildConfig; import io.github.lsposed.manager.Constants; import io.github.lsposed.manager.R; import io.github.lsposed.manager.adapters.AppHelper; @@ -256,6 +258,7 @@ public final class ModuleUtil { public PackageInfo pkg; private String appName; // loaded lazyily private String description; // loaded lazyily + private List scopeList; // loaded lazyily private InstalledModule(PackageInfo pkg, boolean isFramework) { this.app = pkg.applicationInfo; @@ -320,6 +323,20 @@ public final class ModuleUtil { return this.description; } + public List getScopeList() { + if (this.scopeList == null) { + try { + int scopeListResourceId = app.metaData.getInt("xposedscope"); + if (scopeListResourceId != 0) { + scopeList = Arrays.asList(pm.getResourcesForApplication(BuildConfig.APPLICATION_ID).getStringArray(scopeListResourceId)); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return this.scopeList; + } + public PackageInfo getPackageInfo() { return pkg; } diff --git a/app/src/main/res/menu/menu_app_list.xml b/app/src/main/res/menu/menu_app_list.xml index 48ad8c9e..8b97ca7a 100644 --- a/app/src/main/res/menu/menu_app_list.xml +++ b/app/src/main/res/menu/menu_app_list.xml @@ -14,6 +14,10 @@ android:icon="@drawable/ic_settings" app:showAsAction="ifRoom" /> + + 作用域列表保存失败 模块设置 %s\n版本:%s + 推荐 + 未选择任何应用。选择推荐的应用? + 选择推荐的应用? + 推荐的应用 + 由于未选择任何应用,模块 %s 已被禁用。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b37a299f..71cfd6b2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -162,4 +162,9 @@ Failed save scope list Module settings %s\nVersion: %s + Recommended + You did not select any app. Select recommended apps? + Select recommended apps? + Recommended + Module %s has been disabled since no app selected. diff --git a/gradle.properties b/gradle.properties index f8046d4f..be045774 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,22 @@ -androidCompileSdkVersion=30 -androidMinSdkVersion=26 +## For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +#Sat Jan 30 19:20:27 CST 2021 androidTargetSdkVersion=30 androidCompileNdkVersion=22.0.7026061 -android.prefabVersion=1.1.2 apiCode=93 +androidCompileSdkVersion=30 +androidMinSdkVersion=26 +org.gradle.jvmargs=-Xmx2048M android.useAndroidX=true android.enableJetifier=true +android.prefabVersion=1.1.2