Improve icon search
This commit is contained in:
@@ -11,7 +11,7 @@ sealed interface IconPackComponent {
|
||||
}
|
||||
|
||||
sealed interface IconPackAppIcon: IconPackComponent {
|
||||
val packageName: String
|
||||
val packageName: String?
|
||||
val activityName: String?
|
||||
val name: String?
|
||||
val themed: Boolean
|
||||
@@ -59,9 +59,9 @@ data class IconMask(
|
||||
data class AppIcon(
|
||||
val drawable: String,
|
||||
override val iconPack: String,
|
||||
override val packageName: String,
|
||||
override val activityName: String?,
|
||||
override val name: String?,
|
||||
override val packageName: String? = null,
|
||||
override val activityName: String? = null,
|
||||
override val name: String? = null,
|
||||
override val themed: Boolean = false,
|
||||
): IconPackComponent, IconPackAppIcon {
|
||||
override fun toDatabaseEntity(): IconEntity {
|
||||
@@ -80,7 +80,7 @@ data class AppIcon(
|
||||
data class CalendarIcon(
|
||||
val drawables: List<String>,
|
||||
override val iconPack: String,
|
||||
override val packageName: String,
|
||||
override val packageName: String?,
|
||||
override val activityName: String? = null,
|
||||
override val name: String? = null,
|
||||
override val themed: Boolean = false,
|
||||
@@ -102,7 +102,7 @@ data class CalendarIcon(
|
||||
data class ClockIcon(
|
||||
val drawable: String,
|
||||
override val iconPack: String,
|
||||
override val packageName: String,
|
||||
override val packageName: String? = null,
|
||||
override val activityName: String? = null,
|
||||
override val name: String? = null,
|
||||
override val themed: Boolean,
|
||||
@@ -146,7 +146,7 @@ fun Icon(entity: IconEntity): IconPackComponent? {
|
||||
"app" -> AppIcon(
|
||||
drawable = entity.drawable ?: return null,
|
||||
iconPack = entity.iconPack,
|
||||
packageName = entity.packageName ?: return null,
|
||||
packageName = entity.packageName,
|
||||
activityName = entity.activityName,
|
||||
themed = entity.themed,
|
||||
name = entity.name,
|
||||
@@ -155,7 +155,7 @@ fun Icon(entity: IconEntity): IconPackComponent? {
|
||||
drawables = entity.drawable?.split(",") ?: return null,
|
||||
iconPack = entity.iconPack,
|
||||
themed = entity.themed,
|
||||
packageName = entity.packageName ?: return null,
|
||||
packageName = entity.packageName,
|
||||
activityName = entity.activityName,
|
||||
name = entity.name,
|
||||
)
|
||||
@@ -164,7 +164,7 @@ fun Icon(entity: IconEntity): IconPackComponent? {
|
||||
ClockIcon(
|
||||
drawable = entity.drawable!!,
|
||||
iconPack = entity.iconPack,
|
||||
packageName = entity.packageName ?: return null,
|
||||
packageName = entity.packageName,
|
||||
name = entity.name,
|
||||
activityName = entity.activityName,
|
||||
themed = entity.themed,
|
||||
|
||||
@@ -112,6 +112,27 @@ class IconPackManager(
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
suspend fun getIcon(
|
||||
iconPack: String,
|
||||
icon: IconPackAppIcon,
|
||||
allowThemed: Boolean,
|
||||
): LauncherIcon? = withContext(Dispatchers.IO) {
|
||||
val res = try {
|
||||
context.packageManager.getResourcesForApplication(iconPack)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
Log.e("MM20", "Icon pack package $iconPack not found!")
|
||||
return@withContext null
|
||||
}
|
||||
if (icon is CalendarIcon) {
|
||||
return@withContext getIconPackCalendarIcon(icon, res, allowThemed)
|
||||
} else if (icon is AppIcon) {
|
||||
return@withContext getIconPackStaticIcon(icon, res, allowThemed)
|
||||
} else if (icon is ClockIcon) {
|
||||
return@withContext getIconPackClockIcon(icon, res, allowThemed)
|
||||
}
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
suspend fun generateIcon(
|
||||
context: Context,
|
||||
iconPack: String,
|
||||
@@ -448,12 +469,11 @@ class IconPackManager(
|
||||
val drawableQuery = query.replace(" ", "_").lowercase()
|
||||
return iconDao.searchIconPackIcons(
|
||||
drawableQuery = "%$drawableQuery%",
|
||||
componentQuery = "%$query%",
|
||||
nameQuery = "%$query%",
|
||||
iconPack = iconPack?.packageName,
|
||||
).mapNotNull {
|
||||
IconPackAppIcon(it)
|
||||
}
|
||||
}.distinct()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import de.mm20.launcher2.data.customattrs.AdaptifiedLegacyIcon
|
||||
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
|
||||
import de.mm20.launcher2.data.customattrs.CustomIcon
|
||||
import de.mm20.launcher2.data.customattrs.CustomIconPackIcon
|
||||
import de.mm20.launcher2.data.customattrs.LegacyCustomIconPackIcon
|
||||
import de.mm20.launcher2.data.customattrs.CustomThemedIcon
|
||||
import de.mm20.launcher2.data.customattrs.DefaultPlaceholderIcon
|
||||
import de.mm20.launcher2.data.customattrs.ForceThemedIcon
|
||||
@@ -18,6 +19,7 @@ import de.mm20.launcher2.data.customattrs.UnmodifiedSystemDefaultIcon
|
||||
import de.mm20.launcher2.icons.providers.CalendarIconProvider
|
||||
import de.mm20.launcher2.icons.providers.CompatIconProvider
|
||||
import de.mm20.launcher2.icons.providers.CustomIconPackIconProvider
|
||||
import de.mm20.launcher2.icons.providers.LegacyCustomIconPackIconProvider
|
||||
import de.mm20.launcher2.icons.providers.CustomThemedIconProvider
|
||||
import de.mm20.launcher2.icons.providers.DynamicClockIconProvider
|
||||
import de.mm20.launcher2.icons.providers.IconPackIconProvider
|
||||
@@ -44,7 +46,6 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class IconService(
|
||||
@@ -174,6 +175,14 @@ class IconService(
|
||||
)
|
||||
)
|
||||
}
|
||||
if (customIcon is LegacyCustomIconPackIcon) {
|
||||
return listOf(
|
||||
LegacyCustomIconPackIconProvider(
|
||||
customIcon,
|
||||
iconPackManager
|
||||
)
|
||||
)
|
||||
}
|
||||
if (customIcon is CustomThemedIcon) {
|
||||
return listOf(
|
||||
CustomThemedIconProvider(
|
||||
@@ -288,11 +297,14 @@ class IconService(
|
||||
)
|
||||
|
||||
providerOptions.addAll(
|
||||
iconPackIcons.mapNotNull {
|
||||
iconPackIcons.map {
|
||||
val ent = it.toDatabaseEntity()
|
||||
CustomIconPackIcon(
|
||||
iconPackPackage = it.iconPack,
|
||||
iconActivityName = it.activityName,
|
||||
iconPackageName = it.packageName,
|
||||
type = ent.type,
|
||||
drawable = ent.drawable,
|
||||
extras = ent.extras,
|
||||
allowThemed = it.themed,
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -356,20 +368,22 @@ class IconService(
|
||||
val transformations = this.transformations.first()
|
||||
val iconPackIcons = iconPackManager.searchIconPackIcon(query, iconPack).flatMap {
|
||||
val unthemedIcon = if (it.themed) {
|
||||
iconPackManager.getIcon(it.iconPack, it.packageName, it.activityName, false)
|
||||
iconPackManager.getIcon(it.iconPack, it, false)
|
||||
?.transform(transformations)
|
||||
} else null
|
||||
val icon = iconPackManager.getIcon(it.iconPack, it.packageName, it.activityName, true)
|
||||
val icon = iconPackManager.getIcon(it.iconPack, it, true)
|
||||
?.transform(transformations)
|
||||
|
||||
buildList<CustomIconWithPreview> {
|
||||
buildList {
|
||||
val ent = it.toDatabaseEntity()
|
||||
if (icon != null) {
|
||||
add(CustomIconWithPreview(
|
||||
customIcon = CustomIconPackIcon(
|
||||
iconPackPackage = it.iconPack,
|
||||
iconActivityName = it.activityName,
|
||||
iconPackageName = it.packageName,
|
||||
allowThemed = true
|
||||
type = ent.type,
|
||||
drawable = ent.drawable,
|
||||
extras = ent.extras,
|
||||
allowThemed = true,
|
||||
),
|
||||
preview = icon
|
||||
))
|
||||
@@ -378,9 +392,10 @@ class IconService(
|
||||
add(CustomIconWithPreview(
|
||||
customIcon = CustomIconPackIcon(
|
||||
iconPackPackage = it.iconPack,
|
||||
iconActivityName = it.activityName,
|
||||
iconPackageName = it.packageName,
|
||||
allowThemed = false
|
||||
type = ent.type,
|
||||
drawable = ent.drawable,
|
||||
extras = ent.extras,
|
||||
allowThemed = false,
|
||||
),
|
||||
preview = unthemedIcon
|
||||
))
|
||||
|
||||
+166
-122
@@ -30,138 +30,181 @@ class AppFilterIconPackInstaller(
|
||||
) : IconPackInstaller(database) {
|
||||
override suspend fun IconPackInstallerScope.buildIconPack(iconPack: IconPack) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val pkgName = iconPack.packageName
|
||||
|
||||
try {
|
||||
val dynamicClocks = getDynamicClockIcons(pkgName)
|
||||
val parser = getAppfilterParser(pkgName) ?: return@withContext
|
||||
val dynamicClocks = getDynamicClockIcons(iconPack.packageName)
|
||||
|
||||
loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
if (parser.eventType != XmlPullParser.START_TAG) continue
|
||||
when (parser.name) {
|
||||
"item" -> {
|
||||
val component = parser.getAttributeValue(null, "component")
|
||||
?: continue@loop
|
||||
val drawable = parser.getAttributeValue(null, "drawable")
|
||||
?: continue@loop
|
||||
if (component.length <= 14) continue@loop
|
||||
val componentName = ComponentName.unflattenFromString(
|
||||
component.substring(
|
||||
14,
|
||||
component.lastIndex
|
||||
)
|
||||
)
|
||||
?: continue@loop
|
||||
parseAppfilterXml(iconPack, dynamicClocks)
|
||||
parseDrawableXml(iconPack, dynamicClocks)
|
||||
|
||||
val name = parser.getAttributeValue(null, "name")
|
||||
|
||||
|
||||
val icon = if (dynamicClocks.containsKey(drawable)) {
|
||||
ClockIcon(
|
||||
packageName = componentName.packageName,
|
||||
activityName = componentName.shortClassName,
|
||||
iconPack = pkgName,
|
||||
themed = iconPack.themed,
|
||||
name = name,
|
||||
drawable = drawable,
|
||||
config = dynamicClocks[drawable]!!,
|
||||
)
|
||||
} else {
|
||||
AppIcon(
|
||||
packageName = componentName.packageName,
|
||||
activityName = componentName.shortClassName,
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
name = name,
|
||||
themed = iconPack.themed,
|
||||
)
|
||||
}
|
||||
addIcon(icon)
|
||||
}
|
||||
|
||||
"calendar" -> {
|
||||
val component = parser.getAttributeValue(null, "component")
|
||||
?: continue@loop
|
||||
val drawable = parser.getAttributeValue(null, "prefix") ?: continue@loop
|
||||
if (component.length < 14) continue@loop
|
||||
val componentName = ComponentName.unflattenFromString(
|
||||
component.substring(
|
||||
14,
|
||||
component.lastIndex
|
||||
)
|
||||
)
|
||||
?: continue@loop
|
||||
|
||||
val name = parser.getAttributeValue(null, "name")
|
||||
|
||||
val icon = CalendarIcon(
|
||||
packageName = componentName.packageName,
|
||||
activityName = componentName.shortClassName,
|
||||
drawables = (1..31).map { "$drawable$it" },
|
||||
iconPack = pkgName,
|
||||
themed = iconPack.themed,
|
||||
name = name,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
|
||||
"iconback" -> {
|
||||
for (i in 0 until parser.attributeCount) {
|
||||
if (parser.getAttributeName(i).startsWith("img")) {
|
||||
val drawable = parser.getAttributeValue(i)
|
||||
val icon = IconBack(
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"iconupon" -> {
|
||||
for (i in 0 until parser.attributeCount) {
|
||||
if (parser.getAttributeName(i).startsWith("img")) {
|
||||
val drawable = parser.getAttributeValue(i)
|
||||
val icon = IconUpon(
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"iconmask" -> {
|
||||
for (i in 0 until parser.attributeCount) {
|
||||
if (parser.getAttributeName(i).startsWith("img")) {
|
||||
val drawable = parser.getAttributeValue(i)
|
||||
val icon = IconMask(
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"scale" -> {
|
||||
val scale = parser.getAttributeValue(null, "factor")?.toFloatOrNull()
|
||||
?: continue@loop
|
||||
updatePackInfo { it.copy(scale = scale) }
|
||||
}
|
||||
}
|
||||
}
|
||||
parser.close()
|
||||
|
||||
Log.d("MM20", "Icon pack $pkgName has been installed successfully")
|
||||
Log.d("MM20", "Icon pack ${iconPack.packageName} has been installed successfully")
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
Log.e("MM20", "Could not install icon pack $pkgName: package not found.")
|
||||
Log.e(
|
||||
"MM20",
|
||||
"Could not install icon pack ${iconPack.packageName}: package not found."
|
||||
)
|
||||
} catch (e: XmlPullParserException) {
|
||||
CrashReporter.logException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun IconPackInstallerScope.parseAppfilterXml(
|
||||
iconPack: IconPack,
|
||||
dynamicClocks: Map<String, ClockIconConfig>
|
||||
) {
|
||||
val pkgName = iconPack.packageName
|
||||
val parser = getAppfilterParser(pkgName) ?: return
|
||||
|
||||
loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
if (parser.eventType != XmlPullParser.START_TAG) continue
|
||||
when (parser.name) {
|
||||
"item" -> {
|
||||
val component = parser.getAttributeValue(null, "component")
|
||||
?: continue@loop
|
||||
val drawable = parser.getAttributeValue(null, "drawable")
|
||||
?: continue@loop
|
||||
if (component.length <= 14) continue@loop
|
||||
val componentName = ComponentName.unflattenFromString(
|
||||
component.substring(
|
||||
14,
|
||||
component.lastIndex
|
||||
)
|
||||
)
|
||||
?: continue@loop
|
||||
|
||||
val name = parser.getAttributeValue(null, "name")
|
||||
|
||||
|
||||
val icon = if (dynamicClocks.containsKey(drawable)) {
|
||||
ClockIcon(
|
||||
packageName = componentName.packageName,
|
||||
activityName = componentName.shortClassName,
|
||||
iconPack = pkgName,
|
||||
themed = iconPack.themed,
|
||||
name = name,
|
||||
drawable = drawable,
|
||||
config = dynamicClocks[drawable]!!,
|
||||
)
|
||||
} else {
|
||||
AppIcon(
|
||||
packageName = componentName.packageName,
|
||||
activityName = componentName.shortClassName,
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
name = name,
|
||||
themed = iconPack.themed,
|
||||
)
|
||||
}
|
||||
addIcon(icon)
|
||||
}
|
||||
|
||||
"calendar" -> {
|
||||
val component = parser.getAttributeValue(null, "component")
|
||||
?: continue@loop
|
||||
val drawable = parser.getAttributeValue(null, "prefix") ?: continue@loop
|
||||
if (component.length < 14) continue@loop
|
||||
val componentName = ComponentName.unflattenFromString(
|
||||
component.substring(
|
||||
14,
|
||||
component.lastIndex
|
||||
)
|
||||
)
|
||||
?: continue@loop
|
||||
|
||||
val name = parser.getAttributeValue(null, "name")
|
||||
|
||||
val icon = CalendarIcon(
|
||||
packageName = componentName.packageName,
|
||||
activityName = componentName.shortClassName,
|
||||
drawables = (1..31).map { "$drawable$it" },
|
||||
iconPack = pkgName,
|
||||
themed = iconPack.themed,
|
||||
name = name,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
|
||||
"iconback" -> {
|
||||
for (i in 0 until parser.attributeCount) {
|
||||
if (parser.getAttributeName(i).startsWith("img")) {
|
||||
val drawable = parser.getAttributeValue(i)
|
||||
val icon = IconBack(
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"iconupon" -> {
|
||||
for (i in 0 until parser.attributeCount) {
|
||||
if (parser.getAttributeName(i).startsWith("img")) {
|
||||
val drawable = parser.getAttributeValue(i)
|
||||
val icon = IconUpon(
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"iconmask" -> {
|
||||
for (i in 0 until parser.attributeCount) {
|
||||
if (parser.getAttributeName(i).startsWith("img")) {
|
||||
val drawable = parser.getAttributeValue(i)
|
||||
val icon = IconMask(
|
||||
drawable = drawable,
|
||||
iconPack = pkgName,
|
||||
)
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"scale" -> {
|
||||
val scale = parser.getAttributeValue(null, "factor")?.toFloatOrNull()
|
||||
?: continue@loop
|
||||
updatePackInfo { it.copy(scale = scale) }
|
||||
}
|
||||
}
|
||||
}
|
||||
parser.close()
|
||||
}
|
||||
|
||||
private suspend fun IconPackInstallerScope.parseDrawableXml(
|
||||
iconPack: IconPack,
|
||||
dynamicClocks: Map<String, ClockIconConfig>
|
||||
) {
|
||||
val res = context.packageManager.getResourcesForApplication(iconPack.packageName)
|
||||
val xmlId = res.getIdentifier("drawable", "xml", iconPack.packageName)
|
||||
if (xmlId == 0) return
|
||||
val parser = res.getXml(xmlId)
|
||||
loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
if (parser.eventType != XmlPullParser.START_TAG) continue
|
||||
if (parser.name == "item") {
|
||||
val drawable = parser.getAttributeValue(null, "drawable") ?: continue@loop
|
||||
val icon = if (dynamicClocks.containsKey(drawable)) {
|
||||
ClockIcon(
|
||||
iconPack = iconPack.packageName,
|
||||
themed = iconPack.themed,
|
||||
drawable = drawable,
|
||||
config = dynamicClocks[drawable]!!,
|
||||
)
|
||||
} else {
|
||||
AppIcon(
|
||||
drawable = drawable,
|
||||
iconPack = iconPack.packageName,
|
||||
themed = iconPack.themed,
|
||||
)
|
||||
}
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
parser.close()
|
||||
}
|
||||
|
||||
private fun getDynamicClockIcons(packageName: String): Map<String, ClockIconConfig> {
|
||||
val parser = getAppfilterParser(packageName) ?: return emptyMap()
|
||||
val map = mutableMapOf<String, ClockIconConfig>()
|
||||
@@ -190,6 +233,7 @@ class AppFilterIconPackInstaller(
|
||||
)
|
||||
}
|
||||
}
|
||||
parser.close()
|
||||
return map
|
||||
}
|
||||
|
||||
|
||||
+10
-3
@@ -1,7 +1,8 @@
|
||||
package de.mm20.launcher2.icons.providers
|
||||
|
||||
import android.content.ComponentName
|
||||
import de.mm20.launcher2.data.customattrs.CustomIconPackIcon
|
||||
import de.mm20.launcher2.database.entities.IconEntity
|
||||
import de.mm20.launcher2.icons.IconPackAppIcon
|
||||
import de.mm20.launcher2.icons.IconPackManager
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
@@ -11,10 +12,16 @@ class CustomIconPackIconProvider(
|
||||
private val iconPackManager: IconPackManager,
|
||||
) : IconProvider {
|
||||
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||
val ent = IconEntity(
|
||||
type = customIcon.type,
|
||||
drawable = customIcon.drawable,
|
||||
extras = customIcon.extras,
|
||||
iconPack = customIcon.iconPackPackage,
|
||||
)
|
||||
val icon = IconPackAppIcon(ent) ?: return null
|
||||
return iconPackManager.getIcon(
|
||||
customIcon.iconPackPackage,
|
||||
customIcon.iconPackageName,
|
||||
customIcon.iconActivityName,
|
||||
icon,
|
||||
customIcon.allowThemed,
|
||||
)
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package de.mm20.launcher2.icons.providers
|
||||
|
||||
import de.mm20.launcher2.data.customattrs.LegacyCustomIconPackIcon
|
||||
import de.mm20.launcher2.icons.IconPackManager
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
|
||||
@Deprecated("Use CustomIconPackIconProvider instead")
|
||||
class LegacyCustomIconPackIconProvider(
|
||||
private val customIcon: LegacyCustomIconPackIcon,
|
||||
private val iconPackManager: IconPackManager,
|
||||
) : IconProvider {
|
||||
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||
return iconPackManager.getIcon(
|
||||
customIcon.iconPackPackage,
|
||||
customIcon.iconPackageName,
|
||||
customIcon.iconActivityName,
|
||||
customIcon.allowThemed,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user