refactor: remove all Xposed-related features and code from the application and core.

This commit is contained in:
alex5402
2026-01-30 22:36:30 +05:30
parent 4238d87083
commit 46d813480f
31 changed files with 398 additions and 1365 deletions
@@ -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() {
@@ -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);
@@ -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();
}
}
+1 -1
View File
@@ -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)
}
}
}
+1 -15
View File
@@ -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"