mirror of
https://github.com/QM4RS/FridaBox.git
synced 2026-09-16 14:12:11 +02:00
refactor: remove all Xposed-related features and code from the application and core.
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
// IBXposedManagerService.aidl
|
||||
|
||||
package top.niunaijun.blackbox.core.system.pm;
|
||||
|
||||
import java.util.List;
|
||||
import top.niunaijun.blackbox.entity.pm.InstalledModule;
|
||||
|
||||
interface IBXposedManagerService {
|
||||
boolean isXPEnable();
|
||||
void setXPEnable(boolean enable);
|
||||
boolean isModuleEnable(String packageName);
|
||||
void setModuleEnable(String packageName, boolean enable);
|
||||
List<InstalledModule> getInstalledModules();
|
||||
}
|
||||
@@ -46,14 +46,14 @@ import top.niunaijun.blackbox.core.system.user.BUserHandle;
|
||||
import top.niunaijun.blackbox.core.system.user.BUserInfo;
|
||||
import top.niunaijun.blackbox.entity.pm.InstallOption;
|
||||
import top.niunaijun.blackbox.entity.pm.InstallResult;
|
||||
import top.niunaijun.blackbox.entity.pm.InstalledModule;
|
||||
|
||||
import top.niunaijun.blackbox.fake.delegate.ContentProviderDelegate;
|
||||
import top.niunaijun.blackbox.fake.frameworks.BActivityManager;
|
||||
import top.niunaijun.blackbox.fake.frameworks.BJobManager;
|
||||
import top.niunaijun.blackbox.fake.frameworks.BPackageManager;
|
||||
import top.niunaijun.blackbox.fake.frameworks.BStorageManager;
|
||||
import top.niunaijun.blackbox.fake.frameworks.BUserManager;
|
||||
import top.niunaijun.blackbox.fake.frameworks.BXposedManager;
|
||||
|
||||
import top.niunaijun.blackbox.fake.hook.HookManager;
|
||||
import top.niunaijun.blackbox.proxy.ProxyManifest;
|
||||
import top.niunaijun.blackbox.utils.FileUtils;
|
||||
@@ -62,7 +62,7 @@ import top.niunaijun.blackbox.utils.Slog;
|
||||
import top.niunaijun.blackbox.utils.SimpleCrashFix;
|
||||
import top.niunaijun.blackbox.utils.compat.BuildCompat;
|
||||
import top.niunaijun.blackbox.utils.compat.BundleCompat;
|
||||
import top.niunaijun.blackbox.utils.compat.XposedParserCompat;
|
||||
|
||||
import top.niunaijun.blackbox.utils.provider.ProviderCall;
|
||||
import top.niunaijun.blackbox.utils.StackTraceFilter;
|
||||
import top.niunaijun.blackbox.utils.SocialMediaAppCrashPrevention;
|
||||
@@ -239,7 +239,7 @@ public class BlackBoxCore extends ClientConfiguration {
|
||||
ServiceManager.STORAGE_MANAGER,
|
||||
ServiceManager.USER_MANAGER,
|
||||
ServiceManager.JOB_MANAGER,
|
||||
ServiceManager.XPOSED_MANAGER,
|
||||
|
||||
ServiceManager.ACCOUNT_MANAGER,
|
||||
ServiceManager.LOCATION_MANAGER,
|
||||
ServiceManager.NOTIFICATION_MANAGER
|
||||
@@ -270,7 +270,7 @@ public class BlackBoxCore extends ClientConfiguration {
|
||||
ServiceManager.STORAGE_MANAGER,
|
||||
ServiceManager.USER_MANAGER,
|
||||
ServiceManager.JOB_MANAGER,
|
||||
ServiceManager.XPOSED_MANAGER,
|
||||
|
||||
ServiceManager.ACCOUNT_MANAGER,
|
||||
ServiceManager.LOCATION_MANAGER,
|
||||
ServiceManager.NOTIFICATION_MANAGER
|
||||
@@ -303,7 +303,7 @@ public class BlackBoxCore extends ClientConfiguration {
|
||||
ServiceManager.STORAGE_MANAGER,
|
||||
ServiceManager.USER_MANAGER,
|
||||
ServiceManager.JOB_MANAGER,
|
||||
ServiceManager.XPOSED_MANAGER,
|
||||
|
||||
ServiceManager.ACCOUNT_MANAGER,
|
||||
ServiceManager.LOCATION_MANAGER,
|
||||
ServiceManager.NOTIFICATION_MANAGER
|
||||
@@ -1239,58 +1239,9 @@ public class BlackBoxCore extends ClientConfiguration {
|
||||
return getBPackageManager().installPackageAsUser(apk.toString(), InstallOption.installByStorage().makeUriFile(), userId);
|
||||
}
|
||||
|
||||
public InstallResult installXPModule(File apk) {
|
||||
return getBPackageManager().installPackageAsUser(apk.getAbsolutePath(), InstallOption.installByStorage().makeXposed(), BUserHandle.USER_XPOSED);
|
||||
}
|
||||
|
||||
public InstallResult installXPModule(Uri apk) {
|
||||
return getBPackageManager().installPackageAsUser(apk.toString(), InstallOption.installByStorage()
|
||||
.makeXposed()
|
||||
.makeUriFile(), BUserHandle.USER_XPOSED);
|
||||
}
|
||||
|
||||
public InstallResult installXPModule(String packageName) {
|
||||
try {
|
||||
PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 0);
|
||||
String path = packageInfo.applicationInfo.sourceDir;
|
||||
return getBPackageManager().installPackageAsUser(path, InstallOption.installBySystem().makeXposed(), BUserHandle.USER_XPOSED);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return new InstallResult().installError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void uninstallXPModule(String packageName) {
|
||||
uninstallPackage(packageName);
|
||||
}
|
||||
|
||||
public boolean isXPEnable() {
|
||||
return BXposedManager.get().isXPEnable();
|
||||
}
|
||||
|
||||
public void setXPEnable(boolean enable) {
|
||||
BXposedManager.get().setXPEnable(enable);
|
||||
}
|
||||
|
||||
public boolean isXposedModule(File file) {
|
||||
return XposedParserCompat.isXPModule(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
public boolean isInstalledXposedModule(String packageName) {
|
||||
return isInstalled(packageName, BUserHandle.USER_XPOSED);
|
||||
}
|
||||
|
||||
public boolean isModuleEnable(String packageName) {
|
||||
return BXposedManager.get().isModuleEnable(packageName);
|
||||
}
|
||||
|
||||
public void setModuleEnable(String packageName, boolean enable) {
|
||||
BXposedManager.get().setModuleEnable(packageName, enable);
|
||||
}
|
||||
|
||||
public List<InstalledModule> getInstalledXPModules() {
|
||||
return BXposedManager.get().getInstalledModules();
|
||||
}
|
||||
|
||||
public List<ApplicationInfo> getInstalledApplications(int flags, int userId) {
|
||||
return getBPackageManager().getInstalledApplications(flags, userId);
|
||||
@@ -1384,10 +1335,7 @@ public class BlackBoxCore extends ClientConfiguration {
|
||||
return mClientConfiguration.isHideRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHideXposed() {
|
||||
return mClientConfiguration.isHideXposed();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getHostPackageName() {
|
||||
|
||||
@@ -70,10 +70,10 @@ import top.niunaijun.blackbox.core.env.VirtualRuntime;
|
||||
import top.niunaijun.blackbox.core.system.user.BUserHandle;
|
||||
import top.niunaijun.blackbox.entity.AppConfig;
|
||||
import top.niunaijun.blackbox.entity.am.ReceiverData;
|
||||
import top.niunaijun.blackbox.entity.pm.InstalledModule;
|
||||
|
||||
import top.niunaijun.blackbox.fake.delegate.AppInstrumentation;
|
||||
import top.niunaijun.blackbox.fake.delegate.ContentProviderDelegate;
|
||||
import top.niunaijun.blackbox.fake.frameworks.BXposedManager;
|
||||
|
||||
import top.niunaijun.blackbox.fake.hook.HookManager;
|
||||
import top.niunaijun.blackbox.fake.service.HCallbackProxy;
|
||||
import top.niunaijun.blackbox.utils.Reflector;
|
||||
@@ -1055,40 +1055,7 @@ public class BActivityThread extends IBActivityThread.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadXposed(Context context) {
|
||||
String vPackageName = getAppPackageName();
|
||||
String vProcessName = getAppProcessName();
|
||||
if (!TextUtils.isEmpty(vPackageName) && !TextUtils.isEmpty(vProcessName) && BXposedManager.get().isXPEnable()) {
|
||||
assert vPackageName != null;
|
||||
assert vProcessName != null;
|
||||
|
||||
boolean isFirstApplication = vPackageName.equals(vProcessName);
|
||||
|
||||
List<InstalledModule> installedModules = BXposedManager.get().getInstalledModules();
|
||||
for (InstalledModule installedModule : installedModules) {
|
||||
if (!installedModule.enable) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// Remove all PineXposed.loadModule and PineXposed.onPackageLoad calls
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to load Xposed module: " + installedModule.getApplication().packageName
|
||||
+ " (" + installedModule.getApplication().sourceDir + ")\n"
|
||||
+ android.util.Log.getStackTraceString(e);
|
||||
android.util.Log.e("BlackBoxXposed", msg);
|
||||
// Optionally, collect errors for UI display
|
||||
// XposedErrorLogger.logModuleError(installedModule.getApplication().packageName, msg);
|
||||
}
|
||||
}
|
||||
try {
|
||||
// Remove all PineXposed.onPackageLoad calls
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
if (BlackBoxCore.get().isHideXposed()) {
|
||||
NativeCore.hideXposed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder getActivityThread() {
|
||||
|
||||
+1
-3
@@ -16,9 +16,7 @@ public abstract class ClientConfiguration {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isHideXposed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public abstract String getHostPackageName();
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import top.niunaijun.blackbox.utils.compat.BuildCompat;
|
||||
public class AppSystemEnv {
|
||||
private static final List<String> sSystemPackages = new ArrayList<>();
|
||||
private static final List<String> sSuPackages = new ArrayList<>();
|
||||
private static final List<String> sXposedPackages = new ArrayList<>();
|
||||
|
||||
private static final List<String> sPreInstallPackages = new ArrayList<>();
|
||||
|
||||
static {
|
||||
@@ -52,7 +52,7 @@ public class AppSystemEnv {
|
||||
sSuPackages.add("com.thirdparty.superuser");
|
||||
sSuPackages.add("com.yellowes.su");
|
||||
|
||||
sXposedPackages.add("de.robv.android.xposed.installer");
|
||||
|
||||
|
||||
// sPreInstallPackages.add("com.huawei.hwid");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < 29){
|
||||
@@ -73,8 +73,6 @@ public class AppSystemEnv {
|
||||
public static boolean isBlackPackage(String packageName) {
|
||||
if (BlackBoxCore.get().isHideRoot() && sSuPackages.contains(packageName)) {
|
||||
return true;
|
||||
} else if (BlackBoxCore.get().isHideXposed() && sXposedPackages.contains(packageName)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import top.niunaijun.blackbox.core.system.notification.BNotificationManagerServi
|
||||
import top.niunaijun.blackbox.core.system.os.BStorageManagerService;
|
||||
import top.niunaijun.blackbox.core.system.pm.BPackageInstallerService;
|
||||
import top.niunaijun.blackbox.core.system.pm.BPackageManagerService;
|
||||
import top.niunaijun.blackbox.core.system.pm.BXposedManagerService;
|
||||
|
||||
import top.niunaijun.blackbox.core.system.user.BUserHandle;
|
||||
import top.niunaijun.blackbox.core.system.user.BUserManagerService;
|
||||
import top.niunaijun.blackbox.entity.pm.InstallOption;
|
||||
@@ -64,7 +64,7 @@ public class BlackBoxSystem {
|
||||
mServices.add(BJobManagerService.get());
|
||||
mServices.add(BStorageManagerService.get());
|
||||
mServices.add(BPackageInstallerService.get());
|
||||
mServices.add(BXposedManagerService.get());
|
||||
|
||||
mServices.add(BProcessManagerService.get());
|
||||
mServices.add(BAccountManagerService.get());
|
||||
mServices.add(BLocationManagerService.get());
|
||||
|
||||
@@ -13,7 +13,7 @@ import top.niunaijun.blackbox.core.system.location.BLocationManagerService;
|
||||
import top.niunaijun.blackbox.core.system.notification.BNotificationManagerService;
|
||||
import top.niunaijun.blackbox.core.system.os.BStorageManagerService;
|
||||
import top.niunaijun.blackbox.core.system.pm.BPackageManagerService;
|
||||
import top.niunaijun.blackbox.core.system.pm.BXposedManagerService;
|
||||
|
||||
import top.niunaijun.blackbox.core.system.user.BUserManagerService;
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ public class ServiceManager {
|
||||
public static final String PACKAGE_MANAGER = "package_manager";
|
||||
public static final String STORAGE_MANAGER = "storage_manager";
|
||||
public static final String USER_MANAGER = "user_manager";
|
||||
public static final String XPOSED_MANAGER = "xposed_manager";
|
||||
|
||||
public static final String ACCOUNT_MANAGER = "account_manager";
|
||||
public static final String LOCATION_MANAGER = "location_manager";
|
||||
public static final String NOTIFICATION_MANAGER = "notification_manager";
|
||||
@@ -59,7 +59,7 @@ public class ServiceManager {
|
||||
mCaches.put(PACKAGE_MANAGER, BPackageManagerService.get());
|
||||
mCaches.put(STORAGE_MANAGER, BStorageManagerService.get());
|
||||
mCaches.put(USER_MANAGER, BUserManagerService.get());
|
||||
mCaches.put(XPOSED_MANAGER, BXposedManagerService.get());
|
||||
|
||||
mCaches.put(ACCOUNT_MANAGER, BAccountManagerService.get());
|
||||
mCaches.put(LOCATION_MANAGER, BLocationManagerService.get());
|
||||
mCaches.put(NOTIFICATION_MANAGER, BNotificationManagerService.get());
|
||||
@@ -75,7 +75,7 @@ public class ServiceManager {
|
||||
BlackBoxCore.get().getService(PACKAGE_MANAGER);
|
||||
BlackBoxCore.get().getService(STORAGE_MANAGER);
|
||||
BlackBoxCore.get().getService(USER_MANAGER);
|
||||
BlackBoxCore.get().getService(XPOSED_MANAGER);
|
||||
|
||||
BlackBoxCore.get().getService(ACCOUNT_MANAGER);
|
||||
BlackBoxCore.get().getService(LOCATION_MANAGER);
|
||||
BlackBoxCore.get().getService(NOTIFICATION_MANAGER);
|
||||
|
||||
+3
-20
@@ -44,7 +44,7 @@ import top.niunaijun.blackbox.utils.AbiUtils;
|
||||
import top.niunaijun.blackbox.utils.FileUtils;
|
||||
import top.niunaijun.blackbox.utils.Slog;
|
||||
import top.niunaijun.blackbox.utils.compat.PackageParserCompat;
|
||||
import top.niunaijun.blackbox.utils.compat.XposedParserCompat;
|
||||
|
||||
|
||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
||||
|
||||
@@ -533,9 +533,7 @@ public class BPackageManagerService extends IBPackageManagerService.Stub impleme
|
||||
BPackageSettings ps = mPackages.get(packageName);
|
||||
if (ps == null)
|
||||
return;
|
||||
if (ps.installOption.isFlag(InstallOption.FLAG_XPOSED) && userId != BUserHandle.USER_XPOSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isInstalled(packageName, userId)) {
|
||||
return;
|
||||
}
|
||||
@@ -566,15 +564,6 @@ public class BPackageManagerService extends IBPackageManagerService.Stub impleme
|
||||
if (ps == null)
|
||||
return;
|
||||
BProcessManagerService.get().killAllByPackageName(packageName);
|
||||
if (ps.installOption.isFlag(InstallOption.FLAG_XPOSED)) {
|
||||
for (BUserInfo user : BUserManagerService.get().getAllUsers()) {
|
||||
int i = BPackageInstallerService.get().uninstallPackageAsUser(ps, true, user.id);
|
||||
if (i < 0) {
|
||||
continue;
|
||||
}
|
||||
onPackageUninstalled(packageName, true, user.id);
|
||||
}
|
||||
} else {
|
||||
for (Integer userId : ps.getUserIds()) {
|
||||
int i = BPackageInstallerService.get().uninstallPackageAsUser(ps, true, userId);
|
||||
if (i < 0) {
|
||||
@@ -582,7 +571,6 @@ public class BPackageManagerService extends IBPackageManagerService.Stub impleme
|
||||
}
|
||||
onPackageUninstalled(packageName, true, userId);
|
||||
}
|
||||
}
|
||||
mSettings.removePackage(packageName);
|
||||
mComponentResolver.removeAllComponents(ps.pkg);
|
||||
}
|
||||
@@ -680,12 +668,7 @@ public class BPackageManagerService extends IBPackageManagerService.Stub impleme
|
||||
apkFile = new File(file);
|
||||
}
|
||||
|
||||
if (option.isFlag(InstallOption.FLAG_XPOSED) && userId != BUserHandle.USER_XPOSED) {
|
||||
return new InstallResult().installError("Please install the XP module in XP module management");
|
||||
}
|
||||
if (option.isFlag(InstallOption.FLAG_XPOSED) && !XposedParserCompat.isXPModule(apkFile.getAbsolutePath())) {
|
||||
return new InstallResult().installError("not a XP module");
|
||||
}
|
||||
|
||||
|
||||
PackageInfo packageArchiveInfo = BlackBoxCore.getPackageManager().getPackageArchiveInfo(apkFile.getAbsolutePath(), 0);
|
||||
if (packageArchiveInfo == null) {
|
||||
|
||||
@@ -77,11 +77,7 @@ public class BPackageSettings implements Parcelable {
|
||||
}
|
||||
state = new BPackageUserState(state);
|
||||
// xp模块所有用户可见、如果开启的话
|
||||
if (installOption.isFlag(InstallOption.FLAG_XPOSED) &&
|
||||
BXposedManagerService.get().isModuleEnable(pkg.packageName) &&
|
||||
BXposedManagerService.get().isXPEnable()) {
|
||||
state.installed = true;
|
||||
}
|
||||
|
||||
if (userId == BUserHandle.USER_ALL) {
|
||||
state.installed = true;
|
||||
}
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
package top.niunaijun.blackbox.core.system.pm;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.core.util.AtomicFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import top.niunaijun.blackbox.core.env.BEnvironment;
|
||||
import top.niunaijun.blackbox.core.system.ISystemService;
|
||||
import top.niunaijun.blackbox.core.system.user.BUserHandle;
|
||||
import top.niunaijun.blackbox.entity.pm.InstalledModule;
|
||||
import top.niunaijun.blackbox.entity.pm.XposedConfig;
|
||||
import top.niunaijun.blackbox.utils.CloseUtils;
|
||||
import top.niunaijun.blackbox.utils.FileUtils;
|
||||
import top.niunaijun.blackbox.utils.compat.XposedParserCompat;
|
||||
|
||||
/**
|
||||
* updated by alex5402 on 5/2/21.
|
||||
* * ∧_∧
|
||||
* (`・ω・∥
|
||||
* 丶 つ0
|
||||
* しーJ
|
||||
*
|
||||
*/
|
||||
public class BXposedManagerService extends IBXposedManagerService.Stub implements ISystemService, PackageMonitor {
|
||||
private static final BXposedManagerService sService = new BXposedManagerService();
|
||||
|
||||
private XposedConfig mXposedConfig;
|
||||
private final Object mLock = new Object();
|
||||
private BPackageManagerService mPms;
|
||||
private final Map<String, InstalledModule> mCacheModule = new HashMap<>();
|
||||
|
||||
public static BXposedManagerService get() {
|
||||
return sService;
|
||||
}
|
||||
|
||||
public BXposedManagerService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void systemReady() {
|
||||
loadModuleStateLr();
|
||||
mPms = BPackageManagerService.get();
|
||||
mPms.addPackageMonitor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isXPEnable() {
|
||||
synchronized (mLock) {
|
||||
return mXposedConfig.enable;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXPEnable(boolean enable) {
|
||||
synchronized (mLock) {
|
||||
mXposedConfig.enable = enable;
|
||||
saveModuleStateLw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModuleEnable(String packageName) {
|
||||
synchronized (mLock) {
|
||||
Boolean enable = mXposedConfig.moduleState.get(packageName);
|
||||
return enable != null && enable;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModuleEnable(String packageName, boolean enable) {
|
||||
synchronized (mLock) {
|
||||
if (!mPms.isInstalled(packageName, BUserHandle.USER_XPOSED)) {
|
||||
return;
|
||||
}
|
||||
mXposedConfig.moduleState.put(packageName, enable);
|
||||
saveModuleStateLw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstalledModule> getInstalledModules() {
|
||||
List<ApplicationInfo> installedApplications = mPms.getInstalledApplications(PackageManager.GET_META_DATA, BUserHandle.USER_XPOSED);
|
||||
synchronized (mCacheModule) {
|
||||
for (ApplicationInfo installedApplication : installedApplications) {
|
||||
if (mCacheModule.containsKey(installedApplication.packageName))
|
||||
continue;
|
||||
InstalledModule installedModule = XposedParserCompat.parseModule(installedApplication);
|
||||
if (installedModule != null) {
|
||||
mCacheModule.put(installedApplication.packageName, installedModule);
|
||||
}
|
||||
}
|
||||
ArrayList<InstalledModule> installedModules = new ArrayList<>(mCacheModule.values());
|
||||
for (InstalledModule installedModule : installedModules) {
|
||||
installedModule.enable = isModuleEnable(installedModule.packageName);
|
||||
}
|
||||
return installedModules;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadModuleStateLr() {
|
||||
File xpModuleConf = BEnvironment.getXPModuleConf();
|
||||
if (!xpModuleConf.exists()) {
|
||||
mXposedConfig = new XposedConfig();
|
||||
saveModuleStateLw();
|
||||
return;
|
||||
}
|
||||
Parcel parcel = null;
|
||||
try {
|
||||
parcel = FileUtils.readToParcel(xpModuleConf);
|
||||
mXposedConfig = new XposedConfig(parcel);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (parcel != null) {
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveModuleStateLw() {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
AtomicFile atomicFile = new AtomicFile(BEnvironment.getXPModuleConf());
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
mXposedConfig.writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
fileOutputStream = atomicFile.startWrite();
|
||||
FileUtils.writeParcelToOutput(parcel, fileOutputStream);
|
||||
atomicFile.finishWrite(fileOutputStream);
|
||||
} catch (Exception ignored) {
|
||||
atomicFile.failWrite(fileOutputStream);
|
||||
} finally {
|
||||
parcel.recycle();
|
||||
CloseUtils.close(fileOutputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageUninstalled(String packageName, boolean removeApp, int userId) {
|
||||
if (userId != BUserHandle.USER_XPOSED && userId != BUserHandle.USER_ALL) {
|
||||
return;
|
||||
}
|
||||
synchronized (mCacheModule) {
|
||||
mCacheModule.remove(packageName);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mXposedConfig.moduleState.remove(packageName);
|
||||
saveModuleStateLw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageInstalled(String packageName, int userId) {
|
||||
if (userId != BUserHandle.USER_XPOSED && userId != BUserHandle.USER_ALL) {
|
||||
return;
|
||||
}
|
||||
synchronized (mCacheModule) {
|
||||
mCacheModule.remove(packageName);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mXposedConfig.moduleState.put(packageName, false);
|
||||
saveModuleStateLw();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public final class BUserHandle implements Parcelable {
|
||||
public static final int USER_CURRENT_OR_SELF = -3;
|
||||
|
||||
|
||||
public static final int USER_XPOSED = -4;
|
||||
|
||||
|
||||
/**
|
||||
* @hide A user handle to indicate that we would like to send to the current
|
||||
|
||||
@@ -14,7 +14,7 @@ import android.os.Parcelable;
|
||||
public class InstallOption implements Parcelable {
|
||||
public static final int FLAG_SYSTEM = 1;
|
||||
public static final int FLAG_STORAGE = 1 << 1;
|
||||
public static final int FLAG_XPOSED = 1 << 2;
|
||||
|
||||
public static final int FLAG_URI_FILE = 1 << 3;
|
||||
|
||||
public int flags = 0;
|
||||
@@ -31,10 +31,7 @@ public class InstallOption implements Parcelable {
|
||||
return installOption;
|
||||
}
|
||||
|
||||
public InstallOption makeXposed() {
|
||||
this.flags |= FLAG_XPOSED;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public InstallOption makeUriFile() {
|
||||
this.flags |= FLAG_URI_FILE;
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package top.niunaijun.blackbox.entity.pm;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import top.niunaijun.blackbox.BlackBoxCore;
|
||||
import top.niunaijun.blackbox.core.system.user.BUserHandle;
|
||||
|
||||
/**
|
||||
* updated by alex5402 on 5/2/21.
|
||||
* * ∧_∧
|
||||
* (`・ω・∥
|
||||
* 丶 つ0
|
||||
* しーJ
|
||||
*
|
||||
*/
|
||||
public class InstalledModule implements Parcelable {
|
||||
public String packageName;
|
||||
public String name;
|
||||
public String desc;
|
||||
public String main;
|
||||
public boolean enable;
|
||||
|
||||
public InstalledModule() {
|
||||
}
|
||||
|
||||
|
||||
public ApplicationInfo getApplication() {
|
||||
return BlackBoxCore.getBPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA, BUserHandle.USER_XPOSED);
|
||||
}
|
||||
|
||||
public PackageInfo getPackageInfo() {
|
||||
return BlackBoxCore.getBPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA, BUserHandle.USER_XPOSED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(this.packageName);
|
||||
dest.writeString(this.name);
|
||||
dest.writeString(this.desc);
|
||||
dest.writeString(this.main);
|
||||
dest.writeByte(this.enable ? (byte) 1 : (byte) 0);
|
||||
}
|
||||
|
||||
protected InstalledModule(Parcel in) {
|
||||
this.packageName = in.readString();
|
||||
this.name = in.readString();
|
||||
this.desc = in.readString();
|
||||
this.main = in.readString();
|
||||
this.enable = in.readByte() != 0;
|
||||
}
|
||||
|
||||
public static final Creator<InstalledModule> CREATOR = new Creator<InstalledModule>() {
|
||||
@Override
|
||||
public InstalledModule createFromParcel(Parcel source) {
|
||||
return new InstalledModule(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstalledModule[] newArray(int size) {
|
||||
return new InstalledModule[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package top.niunaijun.blackbox.entity.pm;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* updated by alex5402 on 5/2/21.
|
||||
* * ∧_∧
|
||||
* (`・ω・∥
|
||||
* 丶 つ0
|
||||
* しーJ
|
||||
*
|
||||
*/
|
||||
public class XposedConfig implements Parcelable {
|
||||
public boolean enable;
|
||||
public Map<String, Boolean> moduleState = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeByte(this.enable ? (byte) 1 : (byte) 0);
|
||||
dest.writeInt(this.moduleState.size());
|
||||
for (Map.Entry<String, Boolean> entry : this.moduleState.entrySet()) {
|
||||
dest.writeString(entry.getKey());
|
||||
dest.writeValue(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public XposedConfig() {
|
||||
}
|
||||
|
||||
public XposedConfig(Parcel in) {
|
||||
this.enable = in.readByte() != 0;
|
||||
int mModuleStateSize = in.readInt();
|
||||
this.moduleState = new HashMap<String, Boolean>(mModuleStateSize);
|
||||
for (int i = 0; i < mModuleStateSize; i++) {
|
||||
String key = in.readString();
|
||||
Boolean value = (Boolean) in.readValue(Boolean.class.getClassLoader());
|
||||
this.moduleState.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<XposedConfig> CREATOR = new Parcelable.Creator<XposedConfig>() {
|
||||
@Override
|
||||
public XposedConfig createFromParcel(Parcel source) {
|
||||
return new XposedConfig(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XposedConfig[] newArray(int size) {
|
||||
return new XposedConfig[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public final class AppInstrumentation extends BaseInstrumentationDelegate implem
|
||||
@Override
|
||||
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
|
||||
ContextCompat.fix(context);
|
||||
BActivityThread.currentActivityThread().loadXposed(context);
|
||||
|
||||
return super.newApplication(cl, className, context);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package top.niunaijun.blackbox.fake.frameworks;
|
||||
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import top.niunaijun.blackbox.core.system.ServiceManager;
|
||||
import top.niunaijun.blackbox.core.system.pm.IBXposedManagerService;
|
||||
import top.niunaijun.blackbox.entity.pm.InstalledModule;
|
||||
|
||||
/**
|
||||
* updated by alex5402 on 5/2/21.
|
||||
* * ∧_∧
|
||||
* (`・ω・∥
|
||||
* 丶 つ0
|
||||
* しーJ
|
||||
*
|
||||
*/
|
||||
public class BXposedManager extends BlackManager<IBXposedManagerService> {
|
||||
private static final BXposedManager sXposedManager = new BXposedManager();
|
||||
|
||||
public static BXposedManager get() {
|
||||
return sXposedManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getServiceName() {
|
||||
return ServiceManager.XPOSED_MANAGER;
|
||||
}
|
||||
|
||||
public boolean isXPEnable() {
|
||||
try {
|
||||
return getService().isXPEnable();
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setXPEnable(boolean enable) {
|
||||
try {
|
||||
getService().setXPEnable(enable);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isModuleEnable(String packageName) {
|
||||
try {
|
||||
return getService().isModuleEnable(packageName);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setModuleEnable(String packageName, boolean enable) {
|
||||
try {
|
||||
getService().setModuleEnable(packageName, enable);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public List<InstalledModule> getInstalledModules() {
|
||||
try {
|
||||
return getService().getInstalledModules();
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package top.niunaijun.blackbox.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class XposedErrorLogger {
|
||||
private static final Map<String, String> moduleErrors = new HashMap<>();
|
||||
|
||||
public static void logModuleError(String packageName, String error) {
|
||||
moduleErrors.put(packageName, error);
|
||||
}
|
||||
|
||||
public static String getModuleError(String packageName) {
|
||||
return moduleErrors.get(packageName);
|
||||
}
|
||||
|
||||
public static Map<String, String> getAllModuleErrors() {
|
||||
return new HashMap<>(moduleErrors);
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package top.niunaijun.blackbox.utils.compat;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import top.niunaijun.blackbox.BlackBoxCore;
|
||||
import top.niunaijun.blackbox.entity.pm.InstalledModule;
|
||||
import top.niunaijun.blackbox.utils.CloseUtils;
|
||||
|
||||
/**
|
||||
* updated by alex5402 on 5/2/21.
|
||||
* * ∧_∧
|
||||
* (`・ω・∥
|
||||
* 丶 つ0
|
||||
* しーJ
|
||||
*
|
||||
*/
|
||||
public class XposedParserCompat {
|
||||
|
||||
public static InstalledModule parseModule(ApplicationInfo applicationInfo) {
|
||||
try {
|
||||
PackageManager packageManager = BlackBoxCore.getPackageManager();
|
||||
InstalledModule module = new InstalledModule();
|
||||
module.packageName = applicationInfo.packageName;
|
||||
module.enable = false;
|
||||
module.desc = applicationInfo.metaData.getString("xposeddescription");
|
||||
module.name = applicationInfo.loadLabel(packageManager).toString();
|
||||
module.main = readMain(applicationInfo.sourceDir);
|
||||
return module;
|
||||
} catch (RuntimeException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isXPModule(String file) {
|
||||
try {
|
||||
String s = readMain(file);
|
||||
return s != null;
|
||||
} catch (Throwable ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String readMain(String apk) {
|
||||
ZipFile zipFile = null;
|
||||
try {
|
||||
zipFile = new ZipFile(new File(apk));
|
||||
ZipEntry entry = zipFile.getEntry("assets/xposed_init");
|
||||
if (entry == null) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return getInputStreamContent(zipFile.getInputStream(entry)).trim();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
CloseUtils.close(zipFile);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getInputStreamContent(InputStream stream) {
|
||||
BufferedReader reader = null;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(stream));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("#"))
|
||||
continue;
|
||||
builder.append(line).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
CloseUtils.close(reader);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
</activity>
|
||||
<activity android:name=".view.list.ListActivity" />
|
||||
<activity android:name=".view.fake.FakeManagerActivity" />
|
||||
<activity android:name=".view.xp.XpActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".view.main.ShortcutActivity"
|
||||
android:excludeFromRecents="true"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package top.niunaijun.blackboxa.data
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.webkit.URLUtil
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import java.io.File
|
||||
import top.niunaijun.blackbox.BlackBoxCore
|
||||
import top.niunaijun.blackbox.utils.AbiUtils
|
||||
import top.niunaijun.blackboxa.R
|
||||
@@ -12,11 +13,7 @@ import top.niunaijun.blackboxa.app.AppManager
|
||||
import top.niunaijun.blackboxa.bean.AppInfo
|
||||
import top.niunaijun.blackboxa.bean.InstalledAppBean
|
||||
import top.niunaijun.blackboxa.util.MemoryManager
|
||||
import top.niunaijun.blackboxa.util.inflate
|
||||
import top.niunaijun.blackboxa.util.getString
|
||||
import java.io.File
|
||||
import android.webkit.URLUtil
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -24,14 +21,11 @@ import android.webkit.URLUtil
|
||||
* @Author: wukaicheng
|
||||
* @CreateDate: 2021/4/29 23:05
|
||||
*/
|
||||
|
||||
class AppsRepository {
|
||||
val TAG: String = "AppsRepository"
|
||||
private var mInstalledList = mutableListOf<AppInfo>()
|
||||
|
||||
/**
|
||||
* Safely load app label with fallback to package name
|
||||
*/
|
||||
|
||||
/** Safely load app label with fallback to package name */
|
||||
private fun safeLoadAppLabel(applicationInfo: ApplicationInfo): String {
|
||||
return try {
|
||||
BlackBoxCore.getPackageManager().getApplicationLabel(applicationInfo).toString()
|
||||
@@ -40,30 +34,41 @@ class AppsRepository {
|
||||
applicationInfo.packageName // Fallback to package name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely load app icon with fallback to null
|
||||
*/
|
||||
private fun safeLoadAppIcon(applicationInfo: ApplicationInfo): android.graphics.drawable.Drawable? {
|
||||
|
||||
/** Safely load app icon with fallback to null */
|
||||
private fun safeLoadAppIcon(
|
||||
applicationInfo: ApplicationInfo
|
||||
): android.graphics.drawable.Drawable? {
|
||||
return try {
|
||||
// Check if we should skip icon loading to save memory
|
||||
if (MemoryManager.shouldSkipIconLoading()) {
|
||||
Log.w(TAG, "Memory usage high (${MemoryManager.getMemoryUsagePercentage()}%), skipping icon for ${applicationInfo.packageName}")
|
||||
Log.w(
|
||||
TAG,
|
||||
"Memory usage high (${MemoryManager.getMemoryUsagePercentage()}%), skipping icon for ${applicationInfo.packageName}"
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
val icon = BlackBoxCore.getPackageManager().getApplicationIcon(applicationInfo)
|
||||
|
||||
|
||||
// Optimize icon for memory efficiency
|
||||
if (icon is android.graphics.drawable.BitmapDrawable) {
|
||||
val bitmap = icon.bitmap
|
||||
// If icon is too large, scale it down to save memory
|
||||
if (bitmap.width > 96 || bitmap.height > 96) {
|
||||
try {
|
||||
val scaledBitmap = android.graphics.Bitmap.createScaledBitmap(bitmap, 96, 96, true)
|
||||
android.graphics.drawable.BitmapDrawable(BlackBoxCore.getPackageManager().getResourcesForApplication(applicationInfo.packageName), scaledBitmap)
|
||||
val scaledBitmap =
|
||||
android.graphics.Bitmap.createScaledBitmap(bitmap, 96, 96, true)
|
||||
android.graphics.drawable.BitmapDrawable(
|
||||
BlackBoxCore.getPackageManager()
|
||||
.getResourcesForApplication(applicationInfo.packageName),
|
||||
scaledBitmap
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to scale icon for ${applicationInfo.packageName}: ${e.message}")
|
||||
Log.w(
|
||||
TAG,
|
||||
"Failed to scale icon for ${applicationInfo.packageName}: ${e.message}"
|
||||
)
|
||||
icon
|
||||
}
|
||||
} else {
|
||||
@@ -72,7 +77,6 @@ class AppsRepository {
|
||||
} else {
|
||||
icon
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to load icon for ${applicationInfo.packageName}: ${e.message}")
|
||||
null // Fallback to null icon
|
||||
@@ -83,35 +87,45 @@ class AppsRepository {
|
||||
try {
|
||||
synchronized(mInstalledList) {
|
||||
val installedApplications: List<ApplicationInfo> =
|
||||
BlackBoxCore.getPackageManager().getInstalledApplications(0)
|
||||
BlackBoxCore.getPackageManager().getInstalledApplications(0)
|
||||
val installedList = mutableListOf<AppInfo>()
|
||||
|
||||
for (installedApplication in installedApplications) {
|
||||
try {
|
||||
val file = File(installedApplication.sourceDir)
|
||||
|
||||
if ((installedApplication.flags and ApplicationInfo.FLAG_SYSTEM) != 0) continue
|
||||
if ((installedApplication.flags and ApplicationInfo.FLAG_SYSTEM) != 0)
|
||||
continue
|
||||
|
||||
if (!AbiUtils.isSupport(file)) continue
|
||||
|
||||
// Filter out BlackBox apps to prevent cloning
|
||||
if (BlackBoxCore.get().isBlackBoxApp(installedApplication.packageName)) {
|
||||
Log.d(TAG, "Filtering out BlackBox app: ${installedApplication.packageName}")
|
||||
Log.d(
|
||||
TAG,
|
||||
"Filtering out BlackBox app: ${installedApplication.packageName}"
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
val isXpModule = BlackBoxCore.get().isXposedModule(file)
|
||||
val isXpModule = false
|
||||
|
||||
val info = AppInfo(
|
||||
safeLoadAppLabel(installedApplication),
|
||||
safeLoadAppIcon(installedApplication), // Remove the !! operator to allow null icons
|
||||
installedApplication.packageName,
|
||||
installedApplication.sourceDir,
|
||||
isXpModule
|
||||
)
|
||||
val info =
|
||||
AppInfo(
|
||||
safeLoadAppLabel(installedApplication),
|
||||
safeLoadAppIcon(
|
||||
installedApplication
|
||||
), // Remove the !! operator to allow null icons
|
||||
installedApplication.packageName,
|
||||
installedApplication.sourceDir,
|
||||
isXpModule
|
||||
)
|
||||
installedList.add(info)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error processing app ${installedApplication.packageName}: ${e.message}")
|
||||
Log.e(
|
||||
TAG,
|
||||
"Error processing app ${installedApplication.packageName}: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
this.mInstalledList.clear()
|
||||
@@ -123,24 +137,25 @@ class AppsRepository {
|
||||
}
|
||||
|
||||
fun getInstalledAppList(
|
||||
userID: Int,
|
||||
loadingLiveData: MutableLiveData<Boolean>,
|
||||
appsLiveData: MutableLiveData<List<InstalledAppBean>>
|
||||
userID: Int,
|
||||
loadingLiveData: MutableLiveData<Boolean>,
|
||||
appsLiveData: MutableLiveData<List<InstalledAppBean>>
|
||||
) {
|
||||
try {
|
||||
loadingLiveData.postValue(true)
|
||||
synchronized(mInstalledList) {
|
||||
val blackBoxCore = BlackBoxCore.get()
|
||||
Log.d(TAG, mInstalledList.joinToString(","))
|
||||
val newInstalledList = mInstalledList.map {
|
||||
InstalledAppBean(
|
||||
it.name,
|
||||
it.icon, // Remove the !! operator to allow null icons
|
||||
it.packageName,
|
||||
it.sourceDir,
|
||||
blackBoxCore.isInstalled(it.packageName, userID)
|
||||
)
|
||||
}
|
||||
val newInstalledList =
|
||||
mInstalledList.map {
|
||||
InstalledAppBean(
|
||||
it.name,
|
||||
it.icon, // Remove the !! operator to allow null icons
|
||||
it.packageName,
|
||||
it.sourceDir,
|
||||
blackBoxCore.isInstalled(it.packageName, userID)
|
||||
)
|
||||
}
|
||||
appsLiveData.postValue(newInstalledList)
|
||||
loadingLiveData.postValue(false)
|
||||
}
|
||||
@@ -151,87 +166,70 @@ class AppsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstalledModuleList(
|
||||
loadingLiveData: MutableLiveData<Boolean>,
|
||||
appsLiveData: MutableLiveData<List<InstalledAppBean>>
|
||||
) {
|
||||
try {
|
||||
loadingLiveData.postValue(true)
|
||||
synchronized(mInstalledList) {
|
||||
val blackBoxCore = BlackBoxCore.get()
|
||||
val moduleList = mInstalledList.filter {
|
||||
it.isXpModule
|
||||
}.map {
|
||||
InstalledAppBean(
|
||||
it.name,
|
||||
it.icon, // Remove the !! operator to allow null icons
|
||||
it.packageName,
|
||||
it.sourceDir,
|
||||
blackBoxCore.isInstalledXposedModule(it.packageName)
|
||||
)
|
||||
}
|
||||
appsLiveData.postValue(moduleList)
|
||||
loadingLiveData.postValue(false)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in getInstalledModuleList: ${e.message}")
|
||||
loadingLiveData.postValue(false)
|
||||
appsLiveData.postValue(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
fun getVmInstallList(userId: Int, appsLiveData: MutableLiveData<List<AppInfo>>) {
|
||||
try {
|
||||
// Check memory status before starting
|
||||
if (MemoryManager.isMemoryCritical()) {
|
||||
Log.w(TAG, "Memory critical (${MemoryManager.getMemoryUsagePercentage()}%), forcing garbage collection")
|
||||
Log.w(
|
||||
TAG,
|
||||
"Memory critical (${MemoryManager.getMemoryUsagePercentage()}%), forcing garbage collection"
|
||||
)
|
||||
MemoryManager.forceGarbageCollectionIfNeeded()
|
||||
}
|
||||
|
||||
|
||||
val blackBoxCore = BlackBoxCore.get()
|
||||
|
||||
|
||||
// Add debugging for users
|
||||
val users = blackBoxCore.users
|
||||
Log.d(TAG, "getVmInstallList: userId=$userId, total users=${users.size}")
|
||||
users.forEach { user ->
|
||||
Log.d(TAG, "User: id=${user.id}, name=${user.name}")
|
||||
}
|
||||
|
||||
val sortListData =
|
||||
AppManager.mRemarkSharedPreferences.getString("AppList$userId", "")
|
||||
users.forEach { user -> Log.d(TAG, "User: id=${user.id}, name=${user.name}") }
|
||||
|
||||
val sortListData = AppManager.mRemarkSharedPreferences.getString("AppList$userId", "")
|
||||
val sortList = sortListData?.split(",")
|
||||
|
||||
// Add retry mechanism for getting installed applications
|
||||
var applicationList: List<ApplicationInfo>? = null
|
||||
var retryCount = 0
|
||||
val maxRetries = 3
|
||||
|
||||
|
||||
while (applicationList == null && retryCount < maxRetries) {
|
||||
try {
|
||||
applicationList = blackBoxCore.getInstalledApplications(0, userId)
|
||||
if (applicationList == null) {
|
||||
Log.w(TAG, "getVmInstallList: Attempt ${retryCount + 1} returned null, retrying...")
|
||||
Log.w(
|
||||
TAG,
|
||||
"getVmInstallList: Attempt ${retryCount + 1} returned null, retrying..."
|
||||
)
|
||||
retryCount++
|
||||
Thread.sleep(100) // Small delay before retry
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "getVmInstallList: Error getting applications on attempt ${retryCount + 1}: ${e.message}")
|
||||
Log.e(
|
||||
TAG,
|
||||
"getVmInstallList: Error getting applications on attempt ${retryCount + 1}: ${e.message}"
|
||||
)
|
||||
retryCount++
|
||||
if (retryCount < maxRetries) {
|
||||
Thread.sleep(200) // Longer delay for errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add null check for applicationList
|
||||
if (applicationList == null) {
|
||||
Log.e(TAG, "getVmInstallList: applicationList is null for userId=$userId after $maxRetries attempts")
|
||||
Log.e(
|
||||
TAG,
|
||||
"getVmInstallList: applicationList is null for userId=$userId after $maxRetries attempts"
|
||||
)
|
||||
appsLiveData.postValue(emptyList())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Add debugging
|
||||
Log.d(TAG, "getVmInstallList: userId=$userId, applicationList.size=${applicationList.size}")
|
||||
Log.d(
|
||||
TAG,
|
||||
"getVmInstallList: userId=$userId, applicationList.size=${applicationList.size}"
|
||||
)
|
||||
if (applicationList.isNotEmpty()) {
|
||||
Log.d(TAG, "First app: ${applicationList.first().packageName}")
|
||||
} else {
|
||||
@@ -239,19 +237,20 @@ class AppsRepository {
|
||||
}
|
||||
|
||||
val appInfoList = mutableListOf<AppInfo>()
|
||||
|
||||
|
||||
// Sort the application list if sort data exists
|
||||
val sortedApplicationList = if (!sortList.isNullOrEmpty()) {
|
||||
try {
|
||||
applicationList.sortedWith(AppsSortComparator(sortList))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "getVmInstallList: Error sorting applications: ${e.message}")
|
||||
applicationList // Return unsorted list if sorting fails
|
||||
}
|
||||
} else {
|
||||
applicationList
|
||||
}
|
||||
|
||||
val sortedApplicationList =
|
||||
if (!sortList.isNullOrEmpty()) {
|
||||
try {
|
||||
applicationList.sortedWith(AppsSortComparator(sortList))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "getVmInstallList: Error sorting applications: ${e.message}")
|
||||
applicationList // Return unsorted list if sorting fails
|
||||
}
|
||||
} else {
|
||||
applicationList
|
||||
}
|
||||
|
||||
// Process each application with enhanced error handling
|
||||
sortedApplicationList.forEachIndexed { index, applicationInfo ->
|
||||
try {
|
||||
@@ -262,50 +261,73 @@ class AppsRepository {
|
||||
MemoryManager.forceGarbageCollectionIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add null check for applicationInfo
|
||||
if (applicationInfo == null) {
|
||||
Log.w(TAG, "getVmInstallList: Skipping null applicationInfo at index $index")
|
||||
Log.w(
|
||||
TAG,
|
||||
"getVmInstallList: Skipping null applicationInfo at index $index"
|
||||
)
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
|
||||
// Validate package name
|
||||
if (applicationInfo.packageName.isNullOrBlank()) {
|
||||
Log.w(TAG, "getVmInstallList: Skipping app with null/blank package name at index $index")
|
||||
Log.w(
|
||||
TAG,
|
||||
"getVmInstallList: Skipping app with null/blank package name at index $index"
|
||||
)
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
val info = AppInfo(
|
||||
safeLoadAppLabel(applicationInfo),
|
||||
safeLoadAppIcon(applicationInfo), // Remove the !! operator to allow null icons
|
||||
applicationInfo.packageName,
|
||||
applicationInfo.sourceDir ?: "",
|
||||
isInstalledXpModule(applicationInfo.packageName)
|
||||
)
|
||||
|
||||
val info =
|
||||
AppInfo(
|
||||
safeLoadAppLabel(applicationInfo),
|
||||
safeLoadAppIcon(
|
||||
applicationInfo
|
||||
), // Remove the !! operator to allow null icons
|
||||
applicationInfo.packageName,
|
||||
applicationInfo.sourceDir ?: "",
|
||||
false
|
||||
)
|
||||
|
||||
appInfoList.add(info)
|
||||
|
||||
|
||||
// Log progress for large lists
|
||||
if (index > 0 && index % 50 == 0) {
|
||||
Log.d(TAG, "getVmInstallList: Processed $index/${sortedApplicationList.size} apps - ${MemoryManager.getMemoryInfo()}")
|
||||
Log.d(
|
||||
TAG,
|
||||
"getVmInstallList: Processed $index/${sortedApplicationList.size} apps - ${MemoryManager.getMemoryInfo()}"
|
||||
)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "getVmInstallList: Error processing app at index $index (${applicationInfo?.packageName}): ${e.message}")
|
||||
Log.e(
|
||||
TAG,
|
||||
"getVmInstallList: Error processing app at index $index (${applicationInfo?.packageName}): ${e.message}"
|
||||
)
|
||||
// Continue processing other apps instead of failing completely
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "getVmInstallList: processed ${appInfoList.size} apps - ${MemoryManager.getMemoryInfo()}")
|
||||
|
||||
Log.d(
|
||||
TAG,
|
||||
"getVmInstallList: processed ${appInfoList.size} apps - ${MemoryManager.getMemoryInfo()}"
|
||||
)
|
||||
|
||||
// If no virtual apps found, show empty list (correct behavior for new users)
|
||||
// Do NOT load regular installed apps as fallback - this causes the bug
|
||||
if (appInfoList.isEmpty()) {
|
||||
Log.d(TAG, "getVmInstallList: No virtual apps found for userId=$userId, showing empty list (correct for new users)")
|
||||
Log.d(
|
||||
TAG,
|
||||
"getVmInstallList: No virtual apps found for userId=$userId, showing empty list (correct for new users)"
|
||||
)
|
||||
} else {
|
||||
Log.d(TAG, "getVmInstallList: Showing ${appInfoList.size} virtual apps for userId=$userId")
|
||||
Log.d(
|
||||
TAG,
|
||||
"getVmInstallList: Showing ${appInfoList.size} virtual apps for userId=$userId"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Post the result safely
|
||||
try {
|
||||
appsLiveData.postValue(appInfoList)
|
||||
@@ -317,14 +339,19 @@ class AppsRepository {
|
||||
try {
|
||||
appsLiveData.postValue(appInfoList)
|
||||
} catch (e2: Exception) {
|
||||
Log.e(TAG, "getVmInstallList: Fallback posting also failed: ${e2.message}")
|
||||
Log.e(
|
||||
TAG,
|
||||
"getVmInstallList: Fallback posting also failed: ${e2.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e3: Exception) {
|
||||
Log.e(TAG, "getVmInstallList: Could not schedule fallback posting: ${e3.message}")
|
||||
Log.e(
|
||||
TAG,
|
||||
"getVmInstallList: Could not schedule fallback posting: ${e3.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in getVmInstallList: ${e.message}")
|
||||
try {
|
||||
@@ -335,37 +362,30 @@ class AppsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInstalledXpModule(packageName: String): Boolean {
|
||||
return try {
|
||||
BlackBoxCore.get().installedXPModules.forEach {
|
||||
if (packageName == it.packageName) {
|
||||
return@isInstalledXpModule true
|
||||
}
|
||||
}
|
||||
false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking Xposed module: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun installApk(source: String, userId: Int, resultLiveData: MutableLiveData<String>) {
|
||||
try {
|
||||
// Check if this is an attempt to install BlackBox app
|
||||
if (source.contains("blackbox") || source.contains("niunaijun") ||
|
||||
source.contains("vspace") || source.contains("virtual")) {
|
||||
if (source.contains("blackbox") ||
|
||||
source.contains("niunaijun") ||
|
||||
source.contains("vspace") ||
|
||||
source.contains("virtual")
|
||||
) {
|
||||
// Additional check for the actual BlackBox app
|
||||
try {
|
||||
val blackBoxCore = BlackBoxCore.get()
|
||||
val hostPackageName = BlackBoxCore.getHostPkg()
|
||||
|
||||
|
||||
// If it's a file path, try to check the package name
|
||||
if (!URLUtil.isValidUrl(source)) {
|
||||
val file = File(source)
|
||||
if (file.exists()) {
|
||||
val packageInfo = BlackBoxCore.getPackageManager().getPackageArchiveInfo(source, 0)
|
||||
val packageInfo =
|
||||
BlackBoxCore.getPackageManager()
|
||||
.getPackageArchiveInfo(source, 0)
|
||||
if (packageInfo != null && packageInfo.packageName == hostPackageName) {
|
||||
resultLiveData.postValue("Cannot install BlackBox app from within BlackBox. This would create infinite recursion and is not allowed for security reasons.")
|
||||
resultLiveData.postValue(
|
||||
"Cannot install BlackBox app from within BlackBox. This would create infinite recursion and is not allowed for security reasons."
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -374,14 +394,15 @@ class AppsRepository {
|
||||
Log.w(TAG, "Could not verify if this is BlackBox app: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val blackBoxCore = BlackBoxCore.get()
|
||||
val installResult = if (URLUtil.isValidUrl(source)) {
|
||||
val uri = Uri.parse(source)
|
||||
blackBoxCore.installPackageAsUser(uri, userId)
|
||||
} else {
|
||||
blackBoxCore.installPackageAsUser(source, userId)
|
||||
}
|
||||
val installResult =
|
||||
if (URLUtil.isValidUrl(source)) {
|
||||
val uri = Uri.parse(source)
|
||||
blackBoxCore.installPackageAsUser(uri, userId)
|
||||
} else {
|
||||
blackBoxCore.installPackageAsUser(source, userId)
|
||||
}
|
||||
|
||||
if (installResult.success) {
|
||||
updateAppSortList(userId, installResult.packageName, true)
|
||||
@@ -428,10 +449,7 @@ class AppsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒序递归扫描用户,
|
||||
* 如果用户是空的,就删除用户,删除用户备注,删除应用排序列表
|
||||
*/
|
||||
/** 倒序递归扫描用户, 如果用户是空的,就删除用户,删除用户备注,删除应用排序列表 */
|
||||
private fun scanUser() {
|
||||
try {
|
||||
val blackBoxCore = BlackBoxCore.get()
|
||||
@@ -465,8 +483,7 @@ class AppsRepository {
|
||||
*/
|
||||
private fun updateAppSortList(userID: Int, pkg: String, isAdd: Boolean) {
|
||||
try {
|
||||
val savedSortList =
|
||||
AppManager.mRemarkSharedPreferences.getString("AppList$userID", "")
|
||||
val savedSortList = AppManager.mRemarkSharedPreferences.getString("AppList$userID", "")
|
||||
|
||||
val sortList = linkedSetOf<String>()
|
||||
if (savedSortList != null) {
|
||||
@@ -488,14 +505,11 @@ class AppsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存排序后的apk顺序
|
||||
*/
|
||||
/** 保存排序后的apk顺序 */
|
||||
fun updateApkOrder(userID: Int, dataList: List<AppInfo>) {
|
||||
try {
|
||||
AppManager.mRemarkSharedPreferences.edit().apply {
|
||||
putString("AppList$userID",
|
||||
dataList.joinToString(",") { it.packageName })
|
||||
putString("AppList$userID", dataList.joinToString(",") { it.packageName })
|
||||
apply()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package top.niunaijun.blackboxa.data
|
||||
|
||||
import android.net.Uri
|
||||
import android.webkit.URLUtil
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import top.niunaijun.blackbox.BlackBoxCore
|
||||
import top.niunaijun.blackbox.BlackBoxCore.getPackageManager
|
||||
import top.niunaijun.blackboxa.R
|
||||
import top.niunaijun.blackboxa.bean.XpModuleInfo
|
||||
import top.niunaijun.blackboxa.util.getString
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description:
|
||||
* @Author: wukaicheng
|
||||
* @CreateDate: 2021/5/2 20:55
|
||||
*/
|
||||
class XpRepository {
|
||||
fun getInstallModules(modulesLiveData: MutableLiveData<List<XpModuleInfo>>) {
|
||||
val moduleList = BlackBoxCore.get().installedXPModules
|
||||
val result = mutableListOf<XpModuleInfo>()
|
||||
moduleList.forEach {
|
||||
val info = XpModuleInfo(
|
||||
it.name,
|
||||
it.desc,
|
||||
it.packageName,
|
||||
it.packageInfo.versionName.toString(),
|
||||
it.enable,
|
||||
it.application.loadIcon(getPackageManager())
|
||||
)
|
||||
result.add(info)
|
||||
}
|
||||
|
||||
modulesLiveData.postValue(result)
|
||||
}
|
||||
|
||||
fun installModule(source: String, resultLiveData: MutableLiveData<String>) {
|
||||
val blackBoxCore = BlackBoxCore.get()
|
||||
|
||||
val installResult = if (URLUtil.isValidUrl(source)) {
|
||||
val uri = Uri.parse(source)
|
||||
blackBoxCore.installXPModule(uri)
|
||||
} else {
|
||||
//source == packageName
|
||||
blackBoxCore.installXPModule(source)
|
||||
}
|
||||
|
||||
if(installResult.success){
|
||||
resultLiveData.postValue(getString(R.string.install_success))
|
||||
}else{
|
||||
resultLiveData.postValue(getString(R.string.install_fail, installResult.msg))
|
||||
}
|
||||
}
|
||||
|
||||
fun unInstallModule(packageName: String, resultLiveData: MutableLiveData<String>) {
|
||||
BlackBoxCore.get().uninstallXPModule(packageName)
|
||||
resultLiveData.postValue(getString(R.string.remove_success))
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@ package top.niunaijun.blackboxa.util
|
||||
import top.niunaijun.blackboxa.data.AppsRepository
|
||||
import top.niunaijun.blackboxa.data.FakeLocationRepository
|
||||
import top.niunaijun.blackboxa.data.GmsRepository
|
||||
import top.niunaijun.blackboxa.data.XpRepository
|
||||
|
||||
import top.niunaijun.blackboxa.view.apps.AppsFactory
|
||||
import top.niunaijun.blackboxa.view.fake.FakeLocationFactory
|
||||
import top.niunaijun.blackboxa.view.gms.GmsFactory
|
||||
import top.niunaijun.blackboxa.view.list.ListFactory
|
||||
import top.niunaijun.blackboxa.view.xp.XpFactory
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -20,7 +20,7 @@ object InjectionUtil {
|
||||
|
||||
private val appsRepository = AppsRepository()
|
||||
|
||||
private val xpRepository = XpRepository()
|
||||
|
||||
|
||||
private val gmsRepository = GmsRepository()
|
||||
|
||||
@@ -34,9 +34,6 @@ object InjectionUtil {
|
||||
return ListFactory(appsRepository)
|
||||
}
|
||||
|
||||
fun getXpFactory():XpFactory{
|
||||
return XpFactory(xpRepository)
|
||||
}
|
||||
|
||||
fun getGmsFactory():GmsFactory{
|
||||
return GmsFactory(gmsRepository)
|
||||
|
||||
@@ -19,7 +19,6 @@ import top.niunaijun.blackboxa.util.InjectionUtil
|
||||
import top.niunaijun.blackboxa.util.inflate
|
||||
import top.niunaijun.blackboxa.view.base.BaseActivity
|
||||
|
||||
|
||||
class ListActivity : BaseActivity() {
|
||||
|
||||
private val viewBinding: ActivityListBinding by inflate()
|
||||
@@ -36,54 +35,49 @@ class ListActivity : BaseActivity() {
|
||||
|
||||
initToolbar(viewBinding.toolbarLayout.toolbar, R.string.installed_app, true)
|
||||
|
||||
mAdapter = RVAdapter<InstalledAppBean>(this,ListAdapter()).bind(viewBinding.recyclerView).setItemClickListener { _, item, _ ->
|
||||
finishWithResult(item.packageName)
|
||||
}
|
||||
mAdapter =
|
||||
RVAdapter<InstalledAppBean>(this, ListAdapter())
|
||||
.bind(viewBinding.recyclerView)
|
||||
.setItemClickListener { _, item, _ -> finishWithResult(item.packageName) }
|
||||
|
||||
viewBinding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
|
||||
|
||||
initSearchView()
|
||||
initViewModel()
|
||||
}
|
||||
|
||||
private fun initSearchView() {
|
||||
viewBinding.searchView.setOnQueryTextListener(object : SimpleSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
filterApp(newText)
|
||||
return true
|
||||
}
|
||||
viewBinding.searchView.setOnQueryTextListener(
|
||||
object : SimpleSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
filterApp(newText)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextCleared(): Boolean {
|
||||
return true
|
||||
}
|
||||
override fun onQueryTextCleared(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
})
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun initViewModel() {
|
||||
viewModel = ViewModelProvider(this, InjectionUtil.getListFactory()).get(ListViewModel::class.java)
|
||||
val onlyShowXp = intent.getBooleanExtra("onlyShowXp", false)
|
||||
val userID = intent.getIntExtra("userID",0)
|
||||
|
||||
if (onlyShowXp) {
|
||||
viewModel.getInstalledModules()
|
||||
viewBinding.toolbarLayout.toolbar.setTitle(R.string.installed_module)
|
||||
} else {
|
||||
viewModel.getInstallAppList(userID)
|
||||
viewBinding.toolbarLayout.toolbar.setTitle(R.string.installed_app)
|
||||
}
|
||||
viewModel =
|
||||
ViewModelProvider(this, InjectionUtil.getListFactory())
|
||||
.get(ListViewModel::class.java)
|
||||
val userID = intent.getIntExtra("userID", 0)
|
||||
viewModel.getInstallAppList(userID)
|
||||
viewBinding.toolbarLayout.toolbar.setTitle(R.string.installed_app)
|
||||
|
||||
viewModel.loadingLiveData.observe(this) {
|
||||
if (it) {
|
||||
viewBinding.stateView.showLoading()
|
||||
} else {
|
||||
viewBinding.stateView.showContent()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,29 +97,26 @@ class ListActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun filterApp(newText: String) {
|
||||
val newList = this.appList.filter {
|
||||
it.name.contains(newText, true) or it.packageName.contains(newText, true)
|
||||
}
|
||||
val newList =
|
||||
this.appList.filter {
|
||||
it.name.contains(newText, true) or it.packageName.contains(newText, true)
|
||||
}
|
||||
mAdapter.setItems(newList)
|
||||
}
|
||||
|
||||
private val openDocumentedResult = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
it?.run {
|
||||
finishWithResult(it.toString())
|
||||
}
|
||||
}
|
||||
private val openDocumentedResult =
|
||||
registerForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
it?.run { finishWithResult(it.toString()) }
|
||||
}
|
||||
|
||||
private fun finishWithResult(source: String) {
|
||||
intent.putExtra("source", source)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
val imm: InputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
window.peekDecorView()?.run {
|
||||
imm.hideSoftInputFromWindow(windowToken, 0)
|
||||
}
|
||||
window.peekDecorView()?.run { imm.hideSoftInputFromWindow(windowToken, 0) }
|
||||
finish()
|
||||
}
|
||||
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (viewBinding.searchView.isSearchOpen) {
|
||||
viewBinding.searchView.closeSearch()
|
||||
@@ -157,12 +148,10 @@ class ListActivity : BaseActivity() {
|
||||
viewModel.appsLiveData.removeObservers(this)
|
||||
}
|
||||
|
||||
|
||||
companion object{
|
||||
fun start(context: Context,onlyShowXp:Boolean){
|
||||
val intent = Intent(context,ListActivity::class.java)
|
||||
intent.putExtra("onlyShowXp",onlyShowXp)
|
||||
companion object {
|
||||
fun start(context: Context) {
|
||||
val intent = Intent(context, ListActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,10 @@ class ListViewModel(private val repo: AppsRepository) : BaseViewModel() {
|
||||
val loadingLiveData = MutableLiveData<Boolean>()
|
||||
|
||||
fun previewInstalledList() {
|
||||
launchOnUI{
|
||||
repo.previewInstallList()
|
||||
}
|
||||
launchOnUI { repo.previewInstallList() }
|
||||
}
|
||||
|
||||
fun getInstallAppList(userID:Int){
|
||||
launchOnUI {
|
||||
repo.getInstalledAppList(userID,loadingLiveData,appsLiveData)
|
||||
}
|
||||
fun getInstallAppList(userID: Int) {
|
||||
launchOnUI { repo.getInstalledAppList(userID, loadingLiveData, appsLiveData) }
|
||||
}
|
||||
|
||||
fun getInstalledModules() {
|
||||
launchOnUI {
|
||||
repo.getInstalledModuleList(loadingLiveData, appsLiveData)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package top.niunaijun.blackboxa.view.main
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
import top.niunaijun.blackbox.BlackBoxCore
|
||||
import top.niunaijun.blackbox.app.BActivityThread
|
||||
import top.niunaijun.blackbox.app.configuration.AppLifecycleCallback
|
||||
@@ -10,7 +11,6 @@ import top.niunaijun.blackbox.app.configuration.ClientConfiguration
|
||||
import top.niunaijun.blackboxa.app.App
|
||||
import top.niunaijun.blackboxa.app.rocker.RockerManager
|
||||
import top.niunaijun.blackboxa.biz.cache.AppSharedPreferenceDelegate
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -21,7 +21,7 @@ import java.io.File
|
||||
class BlackBoxLoader {
|
||||
|
||||
private var mHideRoot by AppSharedPreferenceDelegate(App.getContext(), false)
|
||||
private var mHideXposed by AppSharedPreferenceDelegate(App.getContext(), false)
|
||||
|
||||
private var mDaemonEnable by AppSharedPreferenceDelegate(App.getContext(), false)
|
||||
private var mShowShortcutPermissionDialog by AppSharedPreferenceDelegate(App.getContext(), true)
|
||||
|
||||
@@ -42,23 +42,6 @@ class BlackBoxLoader {
|
||||
}
|
||||
}
|
||||
|
||||
fun hideXposed(): Boolean {
|
||||
return try {
|
||||
mHideXposed
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting hideXposed: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun invalidHideXposed(hideXposed: Boolean) {
|
||||
try {
|
||||
this.mHideXposed = hideXposed
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error setting hideXposed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun daemonEnable(): Boolean {
|
||||
return try {
|
||||
mDaemonEnable
|
||||
@@ -104,70 +87,100 @@ class BlackBoxLoader {
|
||||
|
||||
fun addLifecycleCallback() {
|
||||
try {
|
||||
BlackBoxCore.get().addAppLifecycleCallback(object : AppLifecycleCallback() {
|
||||
override fun beforeCreateApplication(
|
||||
packageName: String?,
|
||||
processName: String?,
|
||||
context: Context?,
|
||||
userId: Int
|
||||
) {
|
||||
try {
|
||||
Log.d(
|
||||
TAG,
|
||||
"beforeCreateApplication: pkg $packageName, processName $processName,userID:${BActivityThread.getUserId()}"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in beforeCreateApplication: ${e.message}")
|
||||
}
|
||||
}
|
||||
BlackBoxCore.get()
|
||||
.addAppLifecycleCallback(
|
||||
object : AppLifecycleCallback() {
|
||||
override fun beforeCreateApplication(
|
||||
packageName: String?,
|
||||
processName: String?,
|
||||
context: Context?,
|
||||
userId: Int
|
||||
) {
|
||||
try {
|
||||
Log.d(
|
||||
TAG,
|
||||
"beforeCreateApplication: pkg $packageName, processName $processName,userID:${BActivityThread.getUserId()}"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in beforeCreateApplication: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeApplicationOnCreate(
|
||||
packageName: String?,
|
||||
processName: String?,
|
||||
application: Application?,
|
||||
userId: Int
|
||||
) {
|
||||
try {
|
||||
Log.d(TAG, "beforeApplicationOnCreate: pkg $packageName, processName $processName")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in beforeApplicationOnCreate: ${e.message}")
|
||||
}
|
||||
}
|
||||
override fun beforeApplicationOnCreate(
|
||||
packageName: String?,
|
||||
processName: String?,
|
||||
application: Application?,
|
||||
userId: Int
|
||||
) {
|
||||
try {
|
||||
Log.d(
|
||||
TAG,
|
||||
"beforeApplicationOnCreate: pkg $packageName, processName $processName"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"Error in beforeApplicationOnCreate: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterApplicationOnCreate(
|
||||
packageName: String?,
|
||||
processName: String?,
|
||||
application: Application?,
|
||||
userId: Int
|
||||
) {
|
||||
try {
|
||||
Log.d(TAG, "afterApplicationOnCreate: pkg $packageName, processName $processName")
|
||||
RockerManager.init(application,userId)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in afterApplicationOnCreate: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStoragePermissionNeeded(packageName: String?, userId: Int): Boolean {
|
||||
try {
|
||||
Log.w(TAG, "Storage permission needed for launching: $packageName")
|
||||
// Broadcast to request storage permission
|
||||
// The main activity should listen for this and show permission dialog
|
||||
val intent = android.content.Intent("top.niunaijun.blackboxa.REQUEST_STORAGE_PERMISSION")
|
||||
intent.putExtra("package_name", packageName)
|
||||
intent.putExtra("user_id", userId)
|
||||
intent.setPackage(App.getContext().packageName)
|
||||
App.getContext().sendBroadcast(intent)
|
||||
// Return false to NOT block the launch - the app will launch anyway
|
||||
// but the user will be notified to grant permission
|
||||
// Change to 'true' if you want to block launch until permission is granted
|
||||
return false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in onStoragePermissionNeeded: ${e.message}")
|
||||
return false
|
||||
}
|
||||
}
|
||||
})
|
||||
override fun afterApplicationOnCreate(
|
||||
packageName: String?,
|
||||
processName: String?,
|
||||
application: Application?,
|
||||
userId: Int
|
||||
) {
|
||||
try {
|
||||
Log.d(
|
||||
TAG,
|
||||
"afterApplicationOnCreate: pkg $packageName, processName $processName"
|
||||
)
|
||||
RockerManager.init(application, userId)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"Error in afterApplicationOnCreate: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStoragePermissionNeeded(
|
||||
packageName: String?,
|
||||
userId: Int
|
||||
): Boolean {
|
||||
try {
|
||||
Log.w(
|
||||
TAG,
|
||||
"Storage permission needed for launching: $packageName"
|
||||
)
|
||||
// Broadcast to request storage permission
|
||||
// The main activity should listen for this and show
|
||||
// permission dialog
|
||||
val intent =
|
||||
android.content.Intent(
|
||||
"top.niunaijun.blackboxa.REQUEST_STORAGE_PERMISSION"
|
||||
)
|
||||
intent.putExtra("package_name", packageName)
|
||||
intent.putExtra("user_id", userId)
|
||||
intent.setPackage(App.getContext().packageName)
|
||||
App.getContext().sendBroadcast(intent)
|
||||
// Return false to NOT block the launch - the app will
|
||||
// launch anyway
|
||||
// but the user will be notified to grant permission
|
||||
// Change to 'true' if you want to block launch until
|
||||
// permission is granted
|
||||
return false
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"Error in onStoragePermissionNeeded: ${e.message}"
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error adding lifecycle callback: ${e.message}")
|
||||
}
|
||||
@@ -175,58 +188,59 @@ class BlackBoxLoader {
|
||||
|
||||
fun attachBaseContext(context: Context) {
|
||||
try {
|
||||
BlackBoxCore.get().doAttachBaseContext(context, object : ClientConfiguration() {
|
||||
override fun getHostPackageName(): String {
|
||||
return try {
|
||||
context.packageName
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting package name: ${e.message}")
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
BlackBoxCore.get()
|
||||
.doAttachBaseContext(
|
||||
context,
|
||||
object : ClientConfiguration() {
|
||||
override fun getHostPackageName(): String {
|
||||
return try {
|
||||
context.packageName
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting package name: ${e.message}")
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
override fun isHideRoot(): Boolean {
|
||||
return try {
|
||||
mHideRoot
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking hideRoot: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
override fun isHideRoot(): Boolean {
|
||||
return try {
|
||||
mHideRoot
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking hideRoot: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun isHideXposed(): Boolean {
|
||||
return try {
|
||||
mHideXposed
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking hideXposed: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
override fun isEnableDaemonService(): Boolean {
|
||||
return try {
|
||||
mDaemonEnable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking daemonEnable: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnableDaemonService(): Boolean {
|
||||
return try {
|
||||
mDaemonEnable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking daemonEnable: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun requestInstallPackage(file: File?, userId: Int): Boolean {
|
||||
return try {
|
||||
if (file == null) {
|
||||
Log.w(TAG, "requestInstallPackage: file is null")
|
||||
return false
|
||||
}
|
||||
val packageInfo =
|
||||
context.packageManager.getPackageArchiveInfo(file.absolutePath, 0)
|
||||
false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in requestInstallPackage: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
override fun requestInstallPackage(
|
||||
file: File?,
|
||||
userId: Int
|
||||
): Boolean {
|
||||
return try {
|
||||
if (file == null) {
|
||||
Log.w(TAG, "requestInstallPackage: file is null")
|
||||
return false
|
||||
}
|
||||
val packageInfo =
|
||||
context.packageManager.getPackageArchiveInfo(
|
||||
file.absolutePath,
|
||||
0
|
||||
)
|
||||
false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in requestInstallPackage: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in attachBaseContext: ${e.message}")
|
||||
}
|
||||
@@ -235,7 +249,7 @@ class BlackBoxLoader {
|
||||
fun doOnCreate(context: Context) {
|
||||
try {
|
||||
BlackBoxCore.get().doCreate()
|
||||
|
||||
|
||||
// Register callback to refresh app list when services become available
|
||||
try {
|
||||
BlackBoxCore.get().addServiceAvailableCallback {
|
||||
@@ -254,4 +268,4 @@ class BlackBoxLoader {
|
||||
companion object {
|
||||
val TAG: String = BlackBoxLoader::class.java.simpleName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package top.niunaijun.blackboxa.view.setting
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import top.niunaijun.blackbox.BlackBoxCore
|
||||
import top.niunaijun.blackboxa.R
|
||||
import top.niunaijun.blackboxa.app.AppManager
|
||||
import top.niunaijun.blackboxa.util.toast
|
||||
import top.niunaijun.blackboxa.view.gms.GmsManagerActivity
|
||||
import top.niunaijun.blackboxa.view.xp.XpActivity
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -20,37 +17,12 @@ import top.niunaijun.blackboxa.view.xp.XpActivity
|
||||
*/
|
||||
class SettingFragment : PreferenceFragmentCompat() {
|
||||
|
||||
private lateinit var xpEnable: SwitchPreferenceCompat
|
||||
|
||||
private lateinit var xpModule: Preference
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.setting, rootKey)
|
||||
|
||||
xpEnable = findPreference("xp_enable")!!
|
||||
xpEnable.isChecked = BlackBoxCore.get().isXPEnable
|
||||
|
||||
xpEnable.setOnPreferenceChangeListener { _, newValue ->
|
||||
BlackBoxCore.get().isXPEnable = (newValue == true)
|
||||
true
|
||||
}
|
||||
//xp模块跳转
|
||||
xpModule = findPreference("xp_module")!!
|
||||
xpModule.setOnPreferenceClickListener {
|
||||
val intent = Intent(requireActivity(), XpActivity::class.java)
|
||||
requireContext().startActivity(intent)
|
||||
true
|
||||
}
|
||||
initGms()
|
||||
|
||||
invalidHideState{
|
||||
val xpHidePreference: Preference = (findPreference("xp_hide")!!)
|
||||
val hideXposed = AppManager.mBlackBoxLoader.hideXposed()
|
||||
xpHidePreference.setDefaultValue(hideXposed)
|
||||
xpHidePreference
|
||||
}
|
||||
|
||||
invalidHideState{
|
||||
invalidHideState {
|
||||
val rootHidePreference: Preference = (findPreference("root_hide")!!)
|
||||
val hideRoot = AppManager.mBlackBoxLoader.hideRoot()
|
||||
rootHidePreference.setDefaultValue(hideRoot)
|
||||
@@ -85,15 +57,10 @@ class SettingFragment : PreferenceFragmentCompat() {
|
||||
pref.setOnPreferenceChangeListener { preference, newValue ->
|
||||
val tmpHide = (newValue == true)
|
||||
when (preference.key) {
|
||||
"xp_hide" -> {
|
||||
AppManager.mBlackBoxLoader.invalidHideXposed(tmpHide)
|
||||
}
|
||||
|
||||
"root_hide" -> {
|
||||
|
||||
AppManager.mBlackBoxLoader.invalidHideRoot(tmpHide)
|
||||
}
|
||||
|
||||
"daemon_enable" -> {
|
||||
AppManager.mBlackBoxLoader.invalidDaemonEnable(tmpHide)
|
||||
}
|
||||
@@ -103,4 +70,4 @@ class SettingFragment : PreferenceFragmentCompat() {
|
||||
return@setOnPreferenceChangeListener true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
package top.niunaijun.blackboxa.view.xp
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import cbfg.rvadapter.RVAdapter
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import top.niunaijun.blackbox.BlackBoxCore
|
||||
import top.niunaijun.blackboxa.R
|
||||
import top.niunaijun.blackboxa.bean.XpModuleInfo
|
||||
import top.niunaijun.blackboxa.databinding.ActivityXpBinding
|
||||
import top.niunaijun.blackboxa.util.InjectionUtil
|
||||
import top.niunaijun.blackboxa.util.inflate
|
||||
import top.niunaijun.blackboxa.util.toast
|
||||
import top.niunaijun.blackboxa.view.base.LoadingActivity
|
||||
import top.niunaijun.blackboxa.view.list.ListActivity
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description: xposed模块管理界面
|
||||
* @Author: wukaicheng
|
||||
* @CreateDate: 2021/5/2 20:25
|
||||
*/
|
||||
class XpActivity : LoadingActivity() {
|
||||
|
||||
private val viewBinding: ActivityXpBinding by inflate()
|
||||
|
||||
|
||||
private lateinit var viewModel: XpViewModel
|
||||
|
||||
private lateinit var mAdapter: RVAdapter<XpModuleInfo>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(viewBinding.root)
|
||||
initToolbar(viewBinding.toolbarLayout.toolbar, R.string.xp_setting, true)
|
||||
|
||||
viewModel = ViewModelProvider(this, InjectionUtil.getXpFactory()).get(XpViewModel::class.java)
|
||||
|
||||
initRecyclerView()
|
||||
initFab()
|
||||
}
|
||||
|
||||
|
||||
private fun observeLiveData() {
|
||||
|
||||
viewBinding.stateView.showLoading()
|
||||
viewModel.getInstalledModule()
|
||||
viewModel.appsLiveData.observe(this) {
|
||||
if (it.isNullOrEmpty()) {
|
||||
viewBinding.stateView.showEmpty()
|
||||
} else {
|
||||
mAdapter.setItems(it)
|
||||
viewBinding.stateView.showContent()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.resultLiveData.observe(this) {
|
||||
if (!TextUtils.isEmpty(it)) {
|
||||
hideLoading()
|
||||
toast(it)
|
||||
viewModel.getInstalledModule()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initRecyclerView() {
|
||||
|
||||
mAdapter = RVAdapter<XpModuleInfo>(this, XpAdapter()).bind(viewBinding.recyclerView)
|
||||
.setItemClickListener { view, item, position ->
|
||||
item.enable = !item.enable
|
||||
BlackBoxCore.get().setModuleEnable(item.packageName, item.enable)
|
||||
mAdapter.replaceAt(position, item)
|
||||
toast(R.string.restart_module)
|
||||
}.setItemLongClickListener { _, item, _ ->
|
||||
unInstallModule(item.packageName)
|
||||
}
|
||||
|
||||
viewBinding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
viewBinding.stateView.showEmpty()
|
||||
}
|
||||
|
||||
private fun initFab() {
|
||||
viewBinding.fab.setOnClickListener {
|
||||
val intent = Intent(this, ListActivity::class.java)
|
||||
intent.putExtra("onlyShowXp", true)
|
||||
apkPathResult.launch(intent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
observeLiveData()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
viewModel.appsLiveData.value = null
|
||||
viewModel.appsLiveData.removeObservers(this)
|
||||
viewModel.resultLiveData.value = null
|
||||
viewModel.resultLiveData.removeObservers(this)
|
||||
}
|
||||
|
||||
|
||||
private fun unInstallModule(packageName: String) {
|
||||
MaterialDialog(this).show {
|
||||
title(R.string.uninstall_module)
|
||||
message(R.string.uninstall_module_hint)
|
||||
positiveButton(R.string.done) {
|
||||
showLoading()
|
||||
viewModel.unInstallModule(packageName)
|
||||
}
|
||||
negativeButton(R.string.cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun installModule(source: String) {
|
||||
showLoading()
|
||||
viewModel.installModule(source)
|
||||
}
|
||||
|
||||
|
||||
private val apkPathResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
it.data?.let { data ->
|
||||
val source = data.getStringExtra("source")
|
||||
if (source != null) {
|
||||
installModule(source)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
fun start(context: Context) {
|
||||
val intent = Intent(context, XpActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package top.niunaijun.blackboxa.view.xp
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import cbfg.rvadapter.RVHolder
|
||||
import cbfg.rvadapter.RVHolderFactory
|
||||
import top.niunaijun.blackboxa.R
|
||||
import top.niunaijun.blackboxa.bean.XpModuleInfo
|
||||
import top.niunaijun.blackboxa.databinding.ItemXpBinding
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description:
|
||||
* @Author: wukaicheng
|
||||
* @CreateDate: 2021/5/2 21:32
|
||||
*/
|
||||
class XpAdapter : RVHolderFactory() {
|
||||
|
||||
override fun createViewHolder(parent: ViewGroup?, viewType: Int, item: Any): RVHolder<out Any> {
|
||||
return XpVH(inflate(R.layout.item_xp, parent))
|
||||
}
|
||||
|
||||
class XpVH(itemView: View) : RVHolder<XpModuleInfo>(itemView) {
|
||||
|
||||
private val binding = ItemXpBinding.bind(itemView)
|
||||
|
||||
override fun setContent(item: XpModuleInfo, isSelected: Boolean, payload: Any?) {
|
||||
binding.icon.setImageDrawable(item.icon)
|
||||
binding.name.text = item.name
|
||||
binding.desc.text = item.desc
|
||||
binding.enable.isChecked = item.enable
|
||||
binding.enable.setOnCheckedChangeListener { buttonView, _ ->
|
||||
if (buttonView.isPressed) {
|
||||
binding.root.performClick()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package top.niunaijun.blackboxa.view.xp
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import top.niunaijun.blackboxa.data.XpRepository
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description:
|
||||
* @Author: wukaicheng
|
||||
* @CreateDate: 2021/5/2 20:56
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class XpFactory(private val repo:XpRepository): ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return XpViewModel(repo) as T
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package top.niunaijun.blackboxa.view.xp
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import top.niunaijun.blackboxa.bean.XpModuleInfo
|
||||
import top.niunaijun.blackboxa.data.XpRepository
|
||||
import top.niunaijun.blackboxa.view.base.BaseViewModel
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description:
|
||||
* @Author: wukaicheng
|
||||
* @CreateDate: 2021/5/2 20:55
|
||||
*/
|
||||
class XpViewModel(private val repo:XpRepository):BaseViewModel() {
|
||||
|
||||
val appsLiveData = MutableLiveData<List<XpModuleInfo>>()
|
||||
|
||||
val resultLiveData = MutableLiveData<String>()
|
||||
|
||||
fun getInstalledModule() {
|
||||
launchOnUI {
|
||||
repo.getInstallModules(appsLiveData)
|
||||
}
|
||||
}
|
||||
|
||||
fun installModule(source:String) {
|
||||
launchOnUI {
|
||||
repo.installModule(source,resultLiveData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun unInstallModule(packageName: String){
|
||||
launchOnUI {
|
||||
repo.unInstallModule(packageName,resultLiveData)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,7 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory app:title="@string/xp_setting">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="xp_enable"
|
||||
app:title="@string/enable_xposed" />
|
||||
|
||||
<Preference
|
||||
android:dependency="xp_enable"
|
||||
app:key="xp_module"
|
||||
app:summary="@string/jump_module"
|
||||
app:title="@string/module_setting" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/other">
|
||||
|
||||
@@ -23,9 +11,7 @@
|
||||
android:title="@string/gms_manager"
|
||||
android:summary="@string/jump_gms"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="xp_hide"
|
||||
app:title="@string/hide_xposed" />
|
||||
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="root_hide"
|
||||
|
||||
Reference in New Issue
Block a user