mirror of
https://github.com/QM4RS/FridaBox.git
synced 2026-10-14 03:42:22 +02:00
refactor: remove all Xposed-related features and code from the application and core.
This commit is contained in:
@@ -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