diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java index 20f19fddb..d909b1bad 100644 --- a/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java +++ b/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java @@ -203,7 +203,7 @@ public void onRepoLoaded() { modules.forEach((k, v) -> { if (!processedModules.contains(k.first)) { var ver = repoLoader.getModuleLatestVersion(k.first); - if (ver != null && ver.upgradable(v.versionCode, v.versionName)) { + if (!ModuleUtil.isUpdateIgnored(k.first) && ver != null && ver.upgradable(v.versionCode, v.versionName)) { ++count[0]; } processedModules.add(k.first); diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java index 3df1a1c3a..14672727c 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java @@ -343,6 +343,11 @@ public boolean onContextItemSelected(@NonNull MenuItem item) { new Uri.Builder().scheme("lsposed").authority("repo").appendQueryParameter("modulePackageName", selectedModule.packageName).build(), new NavOptions.Builder().setEnterAnim(R.anim.fragment_enter).setExitAnim(R.anim.fragment_exit).setPopEnterAnim(R.anim.fragment_enter_pop).setPopExitAnim(R.anim.fragment_exit_pop).setLaunchSingleTop(true).setPopUpTo(getNavController().getGraph().getStartDestinationId(), false, true).build()); return true; + } else if (itemId == R.id.menu_ignore_update) { + boolean ignored = ModuleUtil.isUpdateIgnored(selectedModule.packageName); + ModuleUtil.setUpdateIgnored(selectedModule.packageName, !ignored); + forEachAdaptor(ModuleAdapter::refresh); + return true; } else if (itemId == R.id.menu_compile_speed) { CompileDialogFragment.speed(getChildFragmentManager(), selectedModule.pkg.applicationInfo); } @@ -583,7 +588,7 @@ public void onLoadCleared(@Nullable Drawable placeholder) { sb.setSpan(foregroundColorSpan, sb.length() - warningText.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); } var ver = repoLoader.getModuleLatestVersion(item.packageName); - if (ver != null && ver.upgradable(item.versionCode, item.versionName)) { + if (!ModuleUtil.isUpdateIgnored(item.packageName) && ver != null && ver.upgradable(item.versionCode, item.versionName)) { if (warningText != null) sb.append("\n"); String recommended = getString(R.string.update_available, ver.versionName); sb.append(recommended); @@ -626,6 +631,13 @@ public void onLoadCleared(@Nullable Drawable placeholder) { } if (repoLoader.getOnlineModule(item.packageName) == null) { menu.removeItem(R.id.menu_repo); + menu.removeItem(R.id.menu_ignore_update); + } else { + var ignoreItem = menu.findItem(R.id.menu_ignore_update); + if (ignoreItem != null) { + boolean ignored = ModuleUtil.isUpdateIgnored(item.packageName); + ignoreItem.setTitle(ignored ? R.string.allow_updates : R.string.ignore_updates); + } } if (item.userId == 0) { var users = ConfigManager.getUsers(); diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java index d1a65b32e..d4180a8df 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java @@ -150,7 +150,7 @@ private void updateRepoSummary() { modules.forEach((k, v) -> { if (!processedModules.contains(k.first)) { var ver = repoLoader.getModuleLatestVersion(k.first); - if (ver != null && ver.upgradable(v.versionCode, v.versionName)) { + if (!ModuleUtil.isUpdateIgnored(k.first) && ver != null && ver.upgradable(v.versionCode, v.versionName)) { ++count[0]; } processedModules.add(k.first); @@ -287,6 +287,9 @@ public RepoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int } RepoLoader.ModuleVersion getUpgradableVer(OnlineModule module) { + if (ModuleUtil.isUpdateIgnored(module.getName())) { + return null; + } ModuleUtil.InstalledModule installedModule = moduleUtil.getModule(module.getName()); if (installedModule != null) { var ver = repoLoader.getModuleLatestVersion(installedModule.packageName); diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java index ef8c5f728..98083a2cc 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java @@ -48,15 +48,18 @@ import org.lsposed.manager.databinding.FragmentSettingsBinding; import org.lsposed.manager.repo.RepoLoader; import org.lsposed.manager.ui.activity.MainActivity; +import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder; import org.lsposed.manager.util.BackupUtils; import org.lsposed.manager.util.CloudflareDNS; import org.lsposed.manager.util.LangList; +import org.lsposed.manager.util.ModuleUtil; import org.lsposed.manager.util.NavUtil; import org.lsposed.manager.util.ShortcutUtil; import org.lsposed.manager.util.ThemeUtil; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.HashSet; import java.util.Locale; import rikka.core.util.ResourceUtils; @@ -146,6 +149,66 @@ private boolean setNotificationPreferenceEnabled(MaterialSwitchPreference notifi return notificationEnabled; } + private void updateIgnoredUpdatesSummary(@Nullable Preference ignoredUpdatesPref) { + if (ignoredUpdatesPref == null) return; + int count = ModuleUtil.getIgnoredUpdates().size(); + if (count == 0) { + ignoredUpdatesPref.setSummary(R.string.settings_ignored_updates_summary_empty); + } else { + ignoredUpdatesPref.setSummary(getString(R.string.settings_ignored_updates_summary, count)); + } + } + + private void showIgnoredUpdatesDialog(@NonNull Preference ignoredUpdatesPref) { + var ignored = new ArrayList<>(ModuleUtil.getIgnoredUpdates()); + if (ignored.isEmpty()) { + new BlurBehindDialogBuilder(requireActivity(), R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons) + .setTitle(R.string.settings_ignored_updates_title) + .setMessage(R.string.settings_ignored_updates_empty) + .setPositiveButton(android.R.string.ok, null) + .show(); + return; + } + + var modules = ModuleUtil.getInstance().getModules(); + var labels = new ArrayList(ignored.size()); + for (var pkg : ignored) { + String label = pkg; + if (modules != null) { + for (var module : modules.values()) { + if (pkg.equals(module.packageName)) { + label = module.getAppName() + " (" + pkg + ")"; + break; + } + } + } + if (label.equals(pkg)) { + label = pkg + " (" + getString(R.string.not_installed) + ")"; + } + labels.add(label); + } + boolean[] checked = new boolean[ignored.size()]; + for (int i = 0; i < checked.length; i++) { + checked[i] = true; + } + + new BlurBehindDialogBuilder(requireActivity(), R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons) + .setTitle(R.string.settings_ignored_updates_title) + .setMultiChoiceItems(labels.toArray(new CharSequence[0]), checked, (dialog, which, isChecked) -> checked[which] = isChecked) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + var updated = new HashSet(); + for (int i = 0; i < checked.length; i++) { + if (checked[i]) { + updated.add(ignored.get(i)); + } + } + ModuleUtil.setIgnoredUpdates(updated); + updateIgnoredUpdatesSummary(ignoredUpdatesPref); + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { final String SYSTEM = "SYSTEM"; @@ -379,6 +442,15 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { return true; }); } + + Preference ignoredUpdates = findPreference("ignored_updates"); + if (ignoredUpdates != null) { + updateIgnoredUpdatesSummary(ignoredUpdates); + ignoredUpdates.setOnPreferenceClickListener(preference -> { + showIgnoredUpdatesDialog(ignoredUpdates); + return true; + }); + } } @NonNull diff --git a/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java b/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java index 1205b37c5..548297ab0 100644 --- a/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java +++ b/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java @@ -57,6 +57,7 @@ public final class ModuleUtil { // xposedminversion below this public static int MIN_MODULE_VERSION = 2; // reject modules with + private static final String PREF_IGNORED_UPDATES = "ignored_module_updates"; private static ModuleUtil instance = null; private final PackageManager pm; private final Set listeners = ConcurrentHashMap.newKeySet(); @@ -85,6 +86,53 @@ public static synchronized ModuleUtil getInstance() { return instance; } + public static boolean isUpdateIgnored(@Nullable String packageName) { + if (packageName == null) return false; + var ignored = App.getPreferences().getStringSet(PREF_IGNORED_UPDATES, Collections.emptySet()); + return ignored != null && ignored.contains(packageName); + } + + @NonNull + public static Set getIgnoredUpdates() { + var ignored = App.getPreferences().getStringSet(PREF_IGNORED_UPDATES, Collections.emptySet()); + if (ignored == null || ignored.isEmpty()) { + return Collections.emptySet(); + } + return new HashSet<>(ignored); + } + + public static void setIgnoredUpdates(@Nullable Set packageNames) { + var pref = App.getPreferences(); + if (packageNames == null || packageNames.isEmpty()) { + pref.edit().remove(PREF_IGNORED_UPDATES).apply(); + notifyIgnoredUpdatesChanged(); + return; + } + pref.edit().putStringSet(PREF_IGNORED_UPDATES, new HashSet<>(packageNames)).apply(); + notifyIgnoredUpdatesChanged(); + } + + public static void setUpdateIgnored(@Nullable String packageName, boolean ignored) { + if (packageName == null) return; + var pref = App.getPreferences(); + var current = pref.getStringSet(PREF_IGNORED_UPDATES, Collections.emptySet()); + var updated = new HashSet<>(current != null ? current : Collections.emptySet()); + if (ignored) { + updated.add(packageName); + } else { + updated.remove(packageName); + } + pref.edit().putStringSet(PREF_IGNORED_UPDATES, updated).apply(); + notifyIgnoredUpdatesChanged(); + } + + private static void notifyIgnoredUpdatesChanged() { + ModuleUtil local = instance; + if (local != null) { + local.listeners.forEach(ModuleListener::onModulesReloaded); + } + } + public static int extractIntPart(String str) { int result = 0, length = str.length(); for (int offset = 0; offset < length; offset++) { diff --git a/app/src/main/res/menu/context_menu_modules.xml b/app/src/main/res/menu/context_menu_modules.xml index e126ec147..556781bed 100644 --- a/app/src/main/res/menu/context_menu_modules.xml +++ b/app/src/main/res/menu/context_menu_modules.xml @@ -29,6 +29,9 @@ + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index dd8cc0592..acc09d4d5 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -198,6 +198,10 @@ JingMatrix 帮助我们把 %s 翻译到你的语言 创建一个能打开寄生管理器的快捷方式 已创建快捷方式 + 已忽略的模块更新 + 已忽略模块:%1$d + 没有忽略的模块 + 没有忽略的模块。 当前默认桌面不支持固定快捷方式 状态通知 显示一个通知以打开寄生管理器 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index dff9e4f4a..99d75af5c 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -195,6 +195,10 @@ 譯者 參與翻譯 幫助我們翻譯 %s 到您的語言 + 已忽略的模組更新 + 已忽略模組:%1$d + 沒有忽略的模組 + 沒有忽略的模組。 建立可以開啟寄生管理員的捷徑 捷徑已釘選 目前的預設啟動器不支援釘選捷徑 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 856c1723a..06b38228c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -191,6 +191,10 @@ 譯者 參與翻譯 幫助我們翻譯 %s 到您的語言 + 已忽略的模組更新 + 已忽略模組:%1$d + 沒有忽略的模組 + 沒有忽略的模組。 建立可以開啟寄生管理員的捷徑 捷徑已釘選 目前的預設啟動器不支援釘選捷徑 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6b4b2694e..aaacb3e21 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,6 +151,8 @@ Xposed module is not activated yet Recommended Update available: %1$s + Ignore updates + Allow updates Module %s has been disabled since no app selected. System Framework Backup @@ -207,6 +209,10 @@ Stable Beta Nightly build + Ignored module updates + Ignored modules: %1$d + No ignored modules + There are no ignored modules. Xposed API call protection Block dynamically loaded module code to use Xposed API, this may break some modules but benefit security diff --git a/app/src/main/res/xml/prefs.xml b/app/src/main/res/xml/prefs.xml index 6b9d8691c..630226c63 100644 --- a/app/src/main/res/xml/prefs.xml +++ b/app/src/main/res/xml/prefs.xml @@ -132,6 +132,11 @@ android:key="update_channel" android:summary="%s" android:title="@string/settings_update_channel" /> +