diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index be50ad66..862144ec 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -111,6 +111,7 @@ dependencies {
implementation(libs.dev.rikka.rikkax.material)
implementation(libs.dev.rikka.rikkax.material.preference)
implementation(libs.me.zhanghai.android.appiconloader)
+ implementation(libs.org.lsposed.hiddenapibypass)
compileOnly(libs.dev.rikka.hidden.stub)
ksp(libs.com.github.liujingxing.rxhttp.compiler)
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fbe9e9da..f0dc34fe 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -9,6 +9,9 @@
+
+
().also {
for (packageInfo in packages) {
- if (packageInfo.packageName in Constants.packagesShouldNotHide) continue
- val label = pm.getApplicationLabel(packageInfo.applicationInfo).toString()
- val icon = hmaApp.appIconLoader.loadIcon(packageInfo.applicationInfo)
- it[packageInfo.packageName] = PackageCache(packageInfo, label, icon)
+ if (packageInfo.info.packageName in Constants.packagesShouldNotHide) continue
+ val label = pm.getApplicationLabel(packageInfo.info.applicationInfo).toString()
+ val icon = hmaApp.appIconLoader.loadIcon(packageInfo.info.applicationInfo)
+ it[packageInfo.info.packageName] = PackageCache(packageInfo.info, label, icon, packageInfo.user)
}
}
}
@@ -113,4 +129,81 @@ object PackageHelper {
fun isSystem(packageName: String): Boolean = runBlocking {
packageCache.first()[packageName]!!.info.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0
}
+
+ fun shellGetUsers(): List {
+ val users = mutableListOf()
+ val result = Shell.cmd("pm list users").exec()
+ if (result.isSuccess) {
+ val lines = result.out
+ for (line in lines) {
+ val trimedLine = line.trim()
+ if (trimedLine.startsWith("UserInfo")) {
+ val infos = trimedLine.substringAfter("UserInfo{").substringBefore("}").split(":")
+ val id = infos[0].toInt()
+ val name = infos[1]
+ users.add(UserInfo(id, name))
+ }
+ }
+ }
+ return users
+ }
+
+ fun grantCrossUserPermissions() {
+ SuUtils.execPrivileged("pm grant ${hmaApp.packageName} android.permission.INTERACT_ACROSS_USERS --user 0")
+ }
+
+ fun getInstalledPackagesFromUser(user: Int, tryGrantPermission: Boolean = false): List {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
+ return pm.getInstalledPackages(0)
+ }
+
+ try {
+ val res = HiddenApiBypass.invoke(PackageManager::class.java, pm, "getInstalledPackagesAsUser", 0, user) as List<*>
+ val packages = mutableListOf()
+
+ for (i in res.indices) {
+ packages += res[i] as PackageInfo
+ }
+
+ return packages.toList()
+ } catch(e: SecurityException) {
+ if (tryGrantPermission) {
+ grantCrossUserPermissions()
+ return getInstalledPackagesFromUser(user)
+ }
+ return emptyList()
+ } catch (e: Exception) {
+ return emptyList()
+ }
+ }
+
+ private fun getInstalledPackagesFromAllUsers(deduplicate: Boolean = true): List {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
+ return pm.getInstalledPackages(0).map {
+ PackageInfoWithUser(it, 0)
+ }
+ }
+
+ val users = shellGetUsers()
+
+ if (users.isEmpty()) {
+ // Fall back to current user
+ return pm.getInstalledPackages(0).map {
+ PackageInfoWithUser(it, 0)
+ }
+ }
+
+ val packages = mutableListOf()
+
+ for (user in users) {
+ val userPackages = getInstalledPackagesFromUser(user.id)
+ for (packageInfo in userPackages) {
+ // Deduplicate
+ if (deduplicate && packages.any { it.info.packageName == packageInfo.packageName }) continue
+ packages += PackageInfoWithUser(packageInfo, user.id)
+ }
+ }
+
+ return packages.toList()
+ }
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a2616f2b..e60464c9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -43,3 +43,4 @@ dev-rikka-rikkax-material = { module = "dev.rikka.rikkax.material:material", ver
dev-rikka-rikkax-material-preference = { module = "dev.rikka.rikkax.material:material-preference", version = "2.0.0" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.5.1" }
me-zhanghai-android-appiconloader = { module = "me.zhanghai.android.appiconloader:appiconloader", version = "1.5.0" }
+org-lsposed-hiddenapibypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version = "4.3" }
\ No newline at end of file