Use imageVectors in badges and fix badge icon colors

This commit is contained in:
MM20 2024-06-16 17:15:57 +02:00
parent 553ab2d9c2
commit a63f434efc
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
11 changed files with 60 additions and 41 deletions

View File

@ -60,6 +60,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.icons.ClockLayer import de.mm20.launcher2.icons.ClockLayer
import de.mm20.launcher2.icons.ClockSublayer import de.mm20.launcher2.icons.ClockSublayer
import de.mm20.launcher2.icons.ClockSublayerRole import de.mm20.launcher2.icons.ClockSublayerRole
@ -239,12 +240,11 @@ fun ShapedLauncherIcon(
val _badge = badge() val _badge = badge()
if (_badge != null) { if (_badge != null) {
Surface( Surface(
shadowElevation = 1.dp,
tonalElevation = 1.dp, tonalElevation = 1.dp,
modifier = Modifier modifier = Modifier
.size(size * 0.33f) .size(size * 0.33f)
.align(Alignment.BottomEnd), .align(Alignment.BottomEnd),
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.tertiary,
shape = CircleShape shape = CircleShape
) { ) {
Box( Box(
@ -255,37 +255,37 @@ fun ShapedLauncherIcon(
val progress by animateFloatAsState(it) val progress by animateFloatAsState(it)
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier.fillMaxSize(0.8f), modifier = Modifier.fillMaxSize(0.8f),
progress = progress, progress = { progress },
strokeWidth = size / 48, strokeWidth = size / 48,
color = MaterialTheme.colorScheme.secondaryContainer color = MaterialTheme.colorScheme.onTertiary
) )
} }
val badgeIconRes = _badge.iconRes
val badgeIcon = _badge.icon val badgeIcon = _badge.icon
val number = _badge.number val number = _badge.number
if (badgeIconRes != null) { if (badgeIcon is BadgeIcon.Vector) {
Image( Icon(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(size / 48), .padding(size / 24),
painter = painterResource(badgeIconRes), imageVector = badgeIcon.imageVector,
contentDescription = null contentDescription = null,
tint = MaterialTheme.colorScheme.onTertiary,
) )
} else if (badgeIcon != null) { } else if (badgeIcon is BadgeIcon.Drawable) {
Canvas( Canvas(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(size / 48) .padding(size / 48)
) { ) {
badgeIcon.setBounds( badgeIcon.drawable.setBounds(
0, 0,
0, 0,
this.size.width.roundToInt(), this.size.width.roundToInt(),
this.size.height.roundToInt() this.size.height.roundToInt()
) )
drawIntoCanvas { drawIntoCanvas {
badgeIcon.draw(it.nativeCanvas) badgeIcon.drawable.draw(it.nativeCanvas)
} }
} }
} else if (number != null && number > 0 && number < 100) { } else if (number != null && number > 0 && number < 100) {

View File

@ -20,6 +20,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowDropDown import androidx.compose.material.icons.rounded.ArrowDropDown
import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.FilterAlt import androidx.compose.material.icons.rounded.FilterAlt
import androidx.compose.material.icons.rounded.Search import androidx.compose.material.icons.rounded.Search
import androidx.compose.material3.Button import androidx.compose.material3.Button
@ -51,6 +52,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.icons.CustomIconWithPreview import de.mm20.launcher2.icons.CustomIconWithPreview
import de.mm20.launcher2.icons.IconPack import de.mm20.launcher2.icons.IconPack
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
@ -106,22 +108,13 @@ fun CustomizeSearchableSheet(
val iconSize = 64.dp val iconSize = 64.dp
val iconSizePx = iconSize.toPixels() val iconSizePx = iconSize.toPixels()
val icon by remember { viewModel.getIcon(iconSizePx.toInt()) }.collectAsState(null) val icon by remember { viewModel.getIcon(iconSizePx.toInt()) }.collectAsState(null)
val primaryColor = MaterialTheme.colorScheme.onSecondary
val badgeDrawable = remember {
InsetDrawable(
AppCompatResources.getDrawable(context, R.drawable.ic_edit),
8
).also {
it.setTint(primaryColor.toArgb())
}
}
ShapedLauncherIcon( ShapedLauncherIcon(
size = iconSize, size = iconSize,
icon = { icon }, icon = { icon },
badge = { badge = {
Badge( Badge(
icon = badgeDrawable icon = BadgeIcon(Icons.Rounded.Edit)
) )
}, },
modifier = Modifier.clickable { modifier = Modifier.clickable {

View File

@ -38,7 +38,7 @@ dependencies {
implementation(libs.bundles.kotlin) implementation(libs.bundles.kotlin)
implementation(libs.androidx.core) implementation(libs.androidx.core)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)
implementation(libs.androidx.compose.materialicons)
implementation(libs.bundles.androidx.lifecycle) implementation(libs.bundles.androidx.lifecycle)
implementation(libs.koin.android) implementation(libs.koin.android)

View File

@ -2,26 +2,36 @@ package de.mm20.launcher2.badges
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.Log import android.util.Log
import androidx.compose.ui.graphics.vector.ImageVector
sealed interface BadgeIcon {
@JvmInline
value class Drawable(val drawable: android.graphics.drawable.Drawable): BadgeIcon
@JvmInline
value class Vector(val imageVector: ImageVector): BadgeIcon
}
fun BadgeIcon(drawable: Drawable): BadgeIcon = BadgeIcon.Drawable(drawable)
fun BadgeIcon(imageVector: ImageVector): BadgeIcon = BadgeIcon.Vector(imageVector)
interface Badge { interface Badge {
val number: Int? val number: Int?
val progress: Float? val progress: Float?
val iconRes: Int? val icon: BadgeIcon?
val icon: Drawable?
} }
fun Badge( fun Badge(
number: Int? = null, number: Int? = null,
progress: Float? = null, progress: Float? = null,
iconRes: Int? = null, icon: BadgeIcon? = null
icon: Drawable? = null ): Badge = MutableBadge(number, progress, icon)
): Badge = MutableBadge(number, progress, iconRes, icon)
internal data class MutableBadge( internal data class MutableBadge(
override var number: Int? = null, override var number: Int? = null,
override var progress: Float? = null, override var progress: Float? = null,
override var iconRes: Int? = null, override var icon: BadgeIcon? = null
override var icon: Drawable? = null
): Badge ): Badge
fun Collection<Badge>.combine(): Badge? { fun Collection<Badge>.combine(): Badge? {
@ -30,7 +40,6 @@ fun Collection<Badge>.combine(): Badge? {
var progresses = 0 var progresses = 0
forEach { forEach {
if (it.icon != null && badge.icon == null) badge.icon = it.icon if (it.icon != null && badge.icon == null) badge.icon = it.icon
if (it.iconRes != null && badge.iconRes == null) badge.iconRes = it.iconRes
it.number?.let { a -> it.number?.let { a ->
badge.number?.let { b -> badge.number = a + b } ?: run { badge.number?.let { b -> badge.number = a + b } ?: run {
badge.number = a badge.number = a

View File

@ -47,7 +47,7 @@ internal class BadgeServiceImpl(
providers += NotificationBadgeProvider() providers += NotificationBadgeProvider()
} }
if (it.cloudFiles) { if (it.cloudFiles) {
providers += CloudBadgeProvider() providers += CloudBadgeProvider(context)
} }
if (it.shortcuts) { if (it.shortcuts) {
providers += AppShortcutBadgeProvider(context) providers += AppShortcutBadgeProvider(context)

View File

@ -3,6 +3,7 @@ package de.mm20.launcher2.badges.providers
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.badges.MutableBadge
import de.mm20.launcher2.graphics.BadgeDrawable import de.mm20.launcher2.graphics.BadgeDrawable
import de.mm20.launcher2.search.AppShortcut import de.mm20.launcher2.search.AppShortcut
@ -28,7 +29,7 @@ class AppShortcutBadgeProvider(
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
return@withContext return@withContext
} }
val badge = MutableBadge(icon = BadgeDrawable(context, icon)) val badge = MutableBadge(icon = BadgeIcon(BadgeDrawable(context, icon)))
send(badge) send(badge)
} }
} else if (packageName != null) { } else if (packageName != null) {
@ -40,7 +41,7 @@ class AppShortcutBadgeProvider(
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
return@withContext return@withContext
} }
val badge = MutableBadge(icon = BadgeDrawable(context, icon)) val badge = MutableBadge(icon = BadgeIcon(BadgeDrawable(context, icon)))
send(badge) send(badge)
} }
} else { } else {

View File

@ -1,6 +1,9 @@
package de.mm20.launcher2.badges.providers package de.mm20.launcher2.badges.providers
import android.content.Context
import androidx.compose.material.icons.Icons
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.badges.MutableBadge
import de.mm20.launcher2.search.File import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Searchable
@ -8,12 +11,14 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
class CloudBadgeProvider: BadgeProvider { class CloudBadgeProvider(
private val context: Context
): BadgeProvider {
override fun getBadge(searchable: Searchable): Flow<Badge?> { override fun getBadge(searchable: Searchable): Flow<Badge?> {
if (searchable is File) { if (searchable is File) {
val iconResId = searchable.providerIconRes val iconResId = searchable.providerIconRes
if (iconResId != null) { if (iconResId != null) {
return flowOf(MutableBadge(iconRes = iconResId)) return flowOf(MutableBadge(icon = BadgeIcon(context.getDrawable(iconResId)!!)))
} }
} }
return flowOf(null) return flowOf(null)

View File

@ -1,6 +1,10 @@
package de.mm20.launcher2.badges.providers package de.mm20.launcher2.badges.providers
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.HourglassBottom
import androidx.compose.material.icons.rounded.VisibilityOff
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.badges.R import de.mm20.launcher2.badges.R
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Searchable
@ -33,7 +37,7 @@ class HiddenItemBadgeProvider(
return hiddenItemKeys.map { keys -> return hiddenItemKeys.map { keys ->
if (searchable.key in keys) { if (searchable.key in keys) {
Badge( Badge(
iconRes = R.drawable.ic_badge_hidden icon = BadgeIcon(Icons.Rounded.VisibilityOff)
) )
} else { } else {
null null

View File

@ -2,6 +2,7 @@ package de.mm20.launcher2.badges.providers
import android.content.Context import android.content.Context
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.badges.MutableBadge
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Searchable
@ -16,7 +17,7 @@ class PluginBadgeProvider(private val context: Context): BadgeProvider {
emit(null) emit(null)
val icon = searchable.getProviderIcon(context) val icon = searchable.getProviderIcon(context)
if (icon != null) { if (icon != null) {
emit(MutableBadge(icon = icon)) emit(MutableBadge(icon = BadgeIcon(icon)))
} }
} }
} }

View File

@ -1,6 +1,9 @@
package de.mm20.launcher2.badges.providers package de.mm20.launcher2.badges.providers
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.HourglassBottom
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.badges.MutableBadge
import de.mm20.launcher2.badges.R import de.mm20.launcher2.badges.R
import de.mm20.launcher2.search.Application import de.mm20.launcher2.search.Application
@ -14,7 +17,7 @@ class SuspendedAppsBadgeProvider : BadgeProvider, KoinComponent {
override fun getBadge(searchable: Searchable): Flow<Badge?> { override fun getBadge(searchable: Searchable): Flow<Badge?> {
return if (searchable is Application && searchable.isSuspended) { return if (searchable is Application && searchable.isSuspended) {
flowOf(MutableBadge(iconRes = R.drawable.ic_badge_suspended)) flowOf(MutableBadge(icon = BadgeIcon(Icons.Rounded.HourglassBottom)))
} else { } else {
flowOf(null) flowOf(null)
} }

View File

@ -1,6 +1,9 @@
package de.mm20.launcher2.badges.providers package de.mm20.launcher2.badges.providers
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Work
import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.badges.BadgeIcon
import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.badges.MutableBadge
import de.mm20.launcher2.badges.R import de.mm20.launcher2.badges.R
import de.mm20.launcher2.search.AppProfile import de.mm20.launcher2.search.AppProfile
@ -15,7 +18,7 @@ class WorkProfileBadgeProvider : BadgeProvider {
if (searchable is Application && searchable.profile == AppProfile.Work || searchable is AppShortcut && searchable.profile == AppProfile.Work) { if (searchable is Application && searchable.profile == AppProfile.Work || searchable is AppShortcut && searchable.profile == AppProfile.Work) {
emit( emit(
MutableBadge( MutableBadge(
iconRes = R.drawable.ic_badge_workprofile icon = BadgeIcon(Icons.Rounded.Work)
) )
) )
} else { } else {