From 1339fc7d3caa45eba31537289035619223c96a28 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Wed, 4 Dec 2024 20:18:08 +0100 Subject: [PATCH] Add more options in the Select menu Allow users to select all/none and automatically include new applications. Close #93 as completed. Co-authored-by: mywalk <66966897+mywalkb@users.noreply.github.com> --- .../org/lsposed/manager/ConfigManager.java | 19 +++++ .../manager/adapters/ScopeAdapter.java | 21 ++++++ app/src/main/res/menu/menu_app_list.xml | 24 +++++-- app/src/main/res/values/strings.xml | 4 ++ .../lsposed/lspd/service/ConfigManager.java | 71 +++++++++++++++---- .../lspd/service/LSPManagerService.java | 10 +++ .../lsposed/lspd/service/LSPosedService.java | 33 +++++++-- .../org/lsposed/lspd/ILSPManagerService.aidl | 4 ++ 8 files changed, 161 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/lsposed/manager/ConfigManager.java b/app/src/main/java/org/lsposed/manager/ConfigManager.java index 983c76fbb50..79a1236a7c9 100644 --- a/app/src/main/java/org/lsposed/manager/ConfigManager.java +++ b/app/src/main/java/org/lsposed/manager/ConfigManager.java @@ -390,4 +390,23 @@ public static int getDex2OatWrapperCompatibility() { return ILSPManagerService.DEX2OAT_CRASHED; } } + + public static boolean getAutoInclude(String packageName) { + try { + return LSPManagerServiceHolder.getService().getAutoInclude(packageName); + } catch (RemoteException e) { + Log.e(App.TAG, Log.getStackTraceString(e)); + return false; + } + } + + public static boolean setAutoInclude(String packageName, boolean enable) { + try { + LSPManagerServiceHolder.getService().setAutoInclude(packageName, enable); + return true; + } catch (RemoteException e) { + Log.e(App.TAG, Log.getStackTraceString(e)); + return false; + } + } } diff --git a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java index 594844f61f6..e080c080de2 100644 --- a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java +++ b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java @@ -292,6 +292,26 @@ public boolean onOptionsItemSelected(MenuItem item) { fragment.showHint(R.string.enable_documentui, true); return false; } + } else if (itemId == R.id.select_all) { + var tmpChkList = new HashSet(ConfigManager.getModuleScope(module.packageName)); + for (AppInfo info : searchList) { + if (info.packageName.equals("android")) { + fragment.showHint(R.string.reboot_required, true, R.string.reboot, v -> ConfigManager.reboot()); + } + tmpChkList.add(info.application); + } + ConfigManager.setModuleScope(module.packageName, module.legacy, tmpChkList); + } else if (itemId == R.id.select_none) { + var tmpChkList = new HashSet(ConfigManager.getModuleScope(module.packageName)); + for (AppInfo info : searchList) { + if (tmpChkList.remove(info.application) && info.packageName.equals("android")) { + fragment.showHint(R.string.reboot_required, true, R.string.reboot, v -> ConfigManager.reboot()); + } + } + ConfigManager.setModuleScope(module.packageName, module.legacy, tmpChkList); + } else if (itemId == R.id.auto_include) { + item.setChecked(!item.isChecked()); + ConfigManager.setAutoInclude(module.packageName, item.isChecked()); } else if (!AppHelper.onOptionsItemSelected(item, preferences)) { return false; } @@ -371,6 +391,7 @@ public void onPrepareOptionsMenu(@NonNull Menu menu) { } case 0 -> menu.findItem(R.id.item_sort_by_name).setChecked(true); } + menu.findItem(R.id.auto_include).setChecked(ConfigManager.getAutoInclude(module.packageName)); } @Override diff --git a/app/src/main/res/menu/menu_app_list.xml b/app/src/main/res/menu/menu_app_list.xml index 35c74cbbab4..42e3e2f042e 100644 --- a/app/src/main/res/menu/menu_app_list.xml +++ b/app/src/main/res/menu/menu_app_list.xml @@ -29,32 +29,44 @@ android:title="@android:string/search_go" /> + android:showAsAction="never" + android:title="@string/menu_select"> + + + + + + + - - - - Denylist Failed to save scope list Version: %1$s + Select Recommended You did not select any app. Select recommended apps? Select recommended apps? + All + None + Auto-Include Xposed module is not activated yet Recommended Update available: %1$s diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java index c4308768673..c90a65aa2e6 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -32,6 +32,7 @@ import android.content.pm.PackageParser; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteStatement; import android.os.Build; import android.os.Bundle; @@ -141,7 +142,9 @@ public int hashCode() { "module_pkg_name text NOT NULL UNIQUE," + "apk_path text NOT NULL, " + "enabled BOOLEAN DEFAULT 0 " + - "CHECK (enabled IN (0, 1))" + + "CHECK (enabled IN (0, 1))," + + "auto_include BOOLEAN DEFAULT 0 " + + "CHECK (auto_include IN (0, 1))" + ");"); private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" + "mid integer," + @@ -420,6 +423,20 @@ private void initDB() { db.compileStatement("UPDATE scope SET app_pkg_name = 'system' WHERE app_pkg_name = 'android';").execute(); db.setVersion(3); }); + case 3: + try { + executeInTransaction(() -> { + db.compileStatement("ALTER TABLE modules ADD COLUMN auto_include BOOLEAN DEFAULT 0 CHECK (auto_include IN (0, 1));").execute(); + db.setVersion(4); + }); + } catch (SQLiteException ex) { + // Fix wrong init code for new column auto_include + if (ex.getMessage().startsWith("duplicate column name: auto_include")) { + db.setVersion(4); + } else { + throw ex; + } + } default: break; } @@ -894,20 +911,7 @@ public boolean removeModuleScope(String packageName, String scopePackageName, in public String[] enabledModules() { - try (Cursor cursor = db.query("modules", new String[]{"module_pkg_name"}, "enabled = 1", null, null, null, null)) { - if (cursor == null) { - Log.e(TAG, "query enabled modules failed"); - return null; - } - int modulePkgNameIdx = cursor.getColumnIndex("module_pkg_name"); - HashSet result = new HashSet<>(); - while (cursor.moveToNext()) { - var pkgName = cursor.getString(modulePkgNameIdx); - if (pkgName.equals("lspd")) continue; - result.add(pkgName); - } - return result.toArray(new String[0]); - } + return listModules("enabled"); } public boolean removeModule(String packageName) { @@ -1223,4 +1227,41 @@ public void exportScopes(ZipOutputStream os) throws IOException { synchronized SharedMemory getPreloadDex() { return ConfigFileManager.getPreloadDex(dexObfuscate); } + + public boolean getAutoInclude(String packageName) { + try (Cursor cursor = db.query("modules", new String[]{"auto_include"}, + "module_pkg_name = ? and auto_include = 1", new String[]{packageName}, null, null, null, null)) { + return cursor == null || cursor.moveToNext(); + } + } + + public boolean setAutoInclude(String packageName, boolean enable) { + boolean changed = executeInTransaction(() -> { + ContentValues values = new ContentValues(); + values.put("auto_include", enable ? 1 : 0); + return db.update("modules", values, "module_pkg_name = ?", new String[]{packageName}) > 0; + }); + return true; + } + + public String[] getAutoIncludeModules() { + return listModules("auto_include"); + } + + private String[] listModules(String column) { + try (Cursor cursor = db.query("modules", new String[]{"module_pkg_name"}, column + " = 1", null, null, null, null)) { + if (cursor == null) { + Log.e(TAG, "query " + column + " modules failed"); + return null; + } + int modulePkgNameIdx = cursor.getColumnIndex("module_pkg_name"); + HashSet result = new HashSet<>(); + while (cursor.moveToNext()) { + var pkgName = cursor.getString(modulePkgNameIdx); + if (pkgName.equals("lspd")) continue; + result.add(pkgName); + } + return result.toArray(new String[0]); + } + } } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java index 582367e15e7..9beaf9e1897 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -556,4 +556,14 @@ public void setLogWatchdog(boolean enabled) { public boolean isLogWatchdogEnabled() { return ConfigManager.getInstance().isLogWatchdogEnabled(); } + + @Override + public boolean setAutoInclude(String packageName, boolean enabled) { + return ConfigManager.getInstance().setAutoInclude(packageName, enabled); + } + + @Override + public boolean getAutoInclude(String packageName) { + return ConfigManager.getInstance().getAutoInclude(packageName); + } } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java index 157e8914699..b2e0e81a214 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java @@ -45,6 +45,7 @@ import android.util.Log; import org.lsposed.daemon.BuildConfig; +import org.lsposed.lspd.models.Application; import java.io.IOException; import java.util.Arrays; @@ -151,8 +152,10 @@ private void dispatchPackageChanged(Intent intent) { if (moduleName != null) { LSPNotificationManager.cancelNotification(UPDATED_CHANNEL_ID, moduleName, userId); } + break; } case Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED -> { + var configManager = ConfigManager.getInstance(); // make sure that the change is for the complete package, not only a // component String[] components = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); @@ -164,10 +167,32 @@ private void dispatchPackageChanged(Intent intent) { // module to send a broadcast when modules that have not been activated are // uninstalled. // If cache not updated, assume it's not xposed module - isXposedModule = ConfigManager.getInstance().updateModuleApkPath(moduleName, ConfigManager.getInstance().getModuleApkPath(applicationInfo), false); - } else if (ConfigManager.getInstance().isUidHooked(uid)) { - // it will auto update obsolete scope from database - ConfigManager.getInstance().updateAppCache(); + isXposedModule = configManager.updateModuleApkPath(moduleName, ConfigManager.getInstance().getModuleApkPath(applicationInfo), false); + } else { + if (configManager.isUidHooked(uid)) { + // it will automatically remove obsolete app from database + configManager.updateAppCache(); + } + if (intentAction.equals(Intent.ACTION_PACKAGE_ADDED) && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + for (String xposedModule : configManager.getAutoIncludeModules()) { + // For Xposed modules with auto_include set, we always add new applications + // to its scope + var list = configManager.getModuleScope(xposedModule); + if (list != null) { + Application scope = new Application(); + scope.packageName = moduleName; + scope.userId = userId; + list.add(scope); + try { + if (!configManager.setModuleScope(xposedModule, list)) { + Log.e(TAG, "failed to set scope for " + xposedModule); + } + } catch(RemoteException re) { + Log.e(TAG, "failed to set scope for " + xposedModule, re); + } + } + } + } } broadcastAndShowNotification(moduleName, userId, intent, isXposedModule); } diff --git a/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl b/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl index ea82be2f6e0..aeded3c624d 100644 --- a/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl +++ b/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl @@ -91,4 +91,8 @@ interface ILSPManagerService { void setLogWatchdog(boolean enable) = 49; boolean isLogWatchdogEnabled() = 50; + + boolean getAutoInclude(String packageName) = 51; + + boolean setAutoInclude(String packageName, boolean enable) = 52; }