Mask private profile apps while private space is locked

This commit is contained in:
MM20
2024-08-13 15:19:24 +02:00
parent 74060d53c3
commit 318c26a439
9 changed files with 238 additions and 70 deletions
+1
View File
@@ -37,6 +37,7 @@ dependencies {
implementation(libs.bundles.kotlin)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.compose.materialicons)
implementation(libs.bundles.androidx.lifecycle)
@@ -12,7 +12,6 @@ import de.mm20.launcher2.search.SearchableSerializer
class FakeApp: Application {
override val componentName: ComponentName = ComponentName(randomString(), randomString())
override val isSystemApp: Boolean = false
override val isSuspended: Boolean = false
override val user: UserHandle = Process.myUserHandle()
override val versionName: String = "1.0"
@@ -7,11 +7,26 @@ import android.content.pm.LauncherApps
import android.os.UserManager
import android.util.Log
import androidx.core.content.getSystemService
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import org.json.JSONObject
internal class LockedPrivateProfileAppSerializer : SearchableSerializer {
override fun serialize(searchable: SavableSearchable): String {
searchable as LockedPrivateProfileApp
val json = JSONObject()
json.put("package", searchable.componentName.packageName)
json.put("activity", searchable.componentName.className)
json.put("user", searchable.userSerialNumber)
return json.toString()
}
override val typePrefix: String
get() = "app"
}
class LauncherAppSerializer : SearchableSerializer {
override fun serialize(searchable: SavableSearchable): String {
searchable as LauncherApp
@@ -33,9 +48,26 @@ class LauncherAppDeserializer(val context: Context) : SearchableDeserializer {
val userManager = context.getSystemService<UserManager>()!!
val userSerial = json.optLong("user")
val user = userManager.getUserForSerialNumber(userSerial) ?: return null
val pkg = json.getString("package")
val activity = json.getString("activity")
val componentName = ComponentName(pkg, activity)
if (isAtLeastApiLevel(35)) {
val launcherUser = launcherApps.getLauncherUserInfo(user) ?: return null
if (launcherUser.userType == UserManager.USER_TYPE_PROFILE_PRIVATE && userManager.isQuietModeEnabled(user)) {
return LockedPrivateProfileApp(
label = context.getString(R.string.app_label_locked_profile),
componentName = componentName,
user = user,
userSerialNumber = userSerial
)
}
}
val intent = Intent().also {
it.component = ComponentName(pkg, json.getString("activity"))
it.component = componentName
}
try {
val launcherActivityInfo = launcherApps.resolveActivity(intent, user) ?: return null
@@ -60,7 +60,7 @@ internal data class LauncherApp(
private val isMainProfile = launcherActivityInfo.user == Process.myUserHandle()
override val isSystemApp: Boolean = launcherActivityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0
private val isSystemApp: Boolean = launcherActivityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0
override val canUninstall: Boolean
get() = !isSystemApp && isMainProfile
@@ -0,0 +1,103 @@
package de.mm20.launcher2.applications
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.pm.LauncherApps
import android.os.Bundle
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Lock
import androidx.core.content.getSystemService
import de.mm20.launcher2.icons.ColorLayer
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.icons.VectorLayer
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.SearchableSerializer
internal data class LockedPrivateProfileApp(
override val label: String,
override val componentName: ComponentName,
override val user: UserHandle,
internal val userSerialNumber: Long,
): Application {
override val isSuspended: Boolean = false
override val versionName: String? = null
override val canUninstall: Boolean = false
override val isPrivate: Boolean = true
override fun uninstall(context: Context) {
// Do nothing
}
override fun openAppDetails(context: Context) {
// Do nothing
}
override val domain: String = LauncherApp.Domain
override val canShareApk: Boolean = false
override val key: String = "${domain}://${componentName.packageName}:${componentName.className}:${userSerialNumber}"
override fun overrideLabel(label: String): SavableSearchable {
// We don't expose custom labels for locked apps
return this
}
override fun launch(context: Context, options: Bundle?): Boolean {
if (!isAtLeastApiLevel(35)) return false
val userManager = context.getSystemService<UserManager>() ?: return false
if (userManager.isQuietModeEnabled(user)) {
userManager.requestQuietModeEnabled(false, user)
return true
}
val launcherApps = context.getSystemService<LauncherApps>() ?: return false
if (isAtLeastApiLevel(31)) {
options?.putInt("android.activity.splashScreenStyle", 1)
}
try {
launcherApps.startMainActivity(
componentName,
user,
null,
options
)
} catch (e: SecurityException) {
Log.e("MM20", "Could not launch app", e)
return false
} catch (e: ActivityNotFoundException) {
Log.e("MM20", "Could not launch app", e)
return false
}
return true
}
override fun getPlaceholderIcon(context: Context): StaticLauncherIcon {
return StaticLauncherIcon(
foregroundLayer = VectorLayer(
vector = Icons.Rounded.Lock,
),
backgroundLayer = ColorLayer(0)
)
}
override suspend fun loadIcon(context: Context, size: Int, themed: Boolean): LauncherIcon? {
return null
}
override fun getSerializer(): SearchableSerializer {
return LockedPrivateProfileAppSerializer()
}
}