Implement custom legacy to adaptive icon transformations

This commit is contained in:
MM20 2022-07-24 20:47:10 +02:00
parent 5bd86e6f95
commit bb211d4d6a
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
12 changed files with 85 additions and 28 deletions

View File

@ -117,6 +117,7 @@ dependencies {
implementation(project(":contacts")) implementation(project(":contacts"))
implementation(project(":crashreporter")) implementation(project(":crashreporter"))
implementation(project(":currencies")) implementation(project(":currencies"))
implementation(project(":customattrs"))
implementation(project(":favorites")) implementation(project(":favorites"))
implementation(project(":files")) implementation(project(":files"))
implementation(project(":g-services")) implementation(project(":g-services"))

View File

@ -12,6 +12,7 @@ import de.mm20.launcher2.badges.badgesModule
import de.mm20.launcher2.calculator.calculatorModule import de.mm20.launcher2.calculator.calculatorModule
import de.mm20.launcher2.calendar.calendarModule import de.mm20.launcher2.calendar.calendarModule
import de.mm20.launcher2.contacts.contactsModule import de.mm20.launcher2.contacts.contactsModule
import de.mm20.launcher2.customattrs.customAttrsModule
import de.mm20.launcher2.debug.Debug import de.mm20.launcher2.debug.Debug
import de.mm20.launcher2.favorites.favoritesModule import de.mm20.launcher2.favorites.favoritesModule
import de.mm20.launcher2.files.filesModule import de.mm20.launcher2.files.filesModule
@ -57,6 +58,7 @@ class LauncherApplication : Application(), CoroutineScope, ImageLoaderFactory {
badgesModule, badgesModule,
calendarModule, calendarModule,
contactsModule, contactsModule,
customAttrsModule,
databaseModule, databaseModule,
favoritesModule, favoritesModule,
filesModule, filesModule,

View File

@ -10,7 +10,8 @@ sealed interface CustomAttribute {
fun toDatabaseEntity(key: String): CustomAttributeEntity fun toDatabaseEntity(key: String): CustomAttributeEntity
companion object { companion object {
internal fun fromDatabaseEntity(entity: CustomAttributeEntity): CustomAttribute? { internal fun fromDatabaseEntity(entity: CustomAttributeEntity?): CustomAttribute? {
if (entity == null) return null
return when (entity.type) { return when (entity.type) {
CustomAttributeType.Label.value -> CustomLabel( CustomAttributeType.Label.value -> CustomLabel(
label = entity.value label = entity.value

View File

@ -1,8 +1,22 @@
package de.mm20.launcher2.customattrs package de.mm20.launcher2.customattrs
import de.mm20.launcher2.database.AppDatabase
import de.mm20.launcher2.search.data.Searchable import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
interface CustomAttributesRepository { interface CustomAttributesRepository {
fun getCustomAttributes(searchable: Searchable, type: CustomAttributeType? = null): Flow<List<CustomAttribute>> fun getCustomIcon(searchable: Searchable): Flow<CustomIcon?>
}
internal class CustomAttributesRepositoryImpl(
private val appDatabase: AppDatabase,
) : CustomAttributesRepository {
override fun getCustomIcon(searchable: Searchable): Flow<CustomIcon?> {
val dao = appDatabase.customAttrsDao()
return dao.getCustomIcon(searchable.key)
.map {
CustomAttribute.fromDatabaseEntity(it) as? CustomIcon
}
}
} }

View File

@ -3,5 +3,5 @@ package de.mm20.launcher2.customattrs
import org.koin.dsl.module import org.koin.dsl.module
val customAttrsModule = module { val customAttrsModule = module {
single<CustomAttributesRepository> { CustomAttributesRepositoryImpl(get()) }
} }

View File

@ -28,6 +28,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun widgetDao(): WidgetDao abstract fun widgetDao(): WidgetDao
abstract fun currencyDao(): CurrencyDao abstract fun currencyDao(): CurrencyDao
abstract fun backupDao(): BackupRestoreDao abstract fun backupDao(): BackupRestoreDao
abstract fun customAttrsDao(): CustomAttrsDao
companion object { companion object {
private var _instance: AppDatabase? = null private var _instance: AppDatabase? = null

View File

@ -0,0 +1,12 @@
package de.mm20.launcher2.database
import androidx.room.Dao
import androidx.room.Query
import de.mm20.launcher2.database.entities.CustomAttributeEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface CustomAttrsDao {
@Query("SELECT * FROM CustomAttributes WHERE type = 'icon' AND key = :key LIMIT 1")
fun getCustomIcon(key: String) : Flow<CustomAttributeEntity?>
}

View File

@ -53,5 +53,6 @@ dependencies {
implementation(project(":search")) implementation(project(":search"))
implementation(project(":applications")) implementation(project(":applications"))
implementation(project(":crashreporter")) implementation(project(":crashreporter"))
implementation(project(":customattrs"))
} }

View File

@ -5,6 +5,9 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.util.LruCache import android.util.LruCache
import de.mm20.launcher2.customattrs.AdaptifiedLegacyIcon
import de.mm20.launcher2.customattrs.CustomAttributesRepository
import de.mm20.launcher2.customattrs.CustomIcon
import de.mm20.launcher2.icons.providers.* import de.mm20.launcher2.icons.providers.*
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation
@ -19,7 +22,8 @@ import kotlinx.coroutines.launch
class IconRepository( class IconRepository(
val context: Context, val context: Context,
private val iconPackManager: IconPackManager, private val iconPackManager: IconPackManager,
private val dataStore: LauncherDataStore private val dataStore: LauncherDataStore,
private val customAttributesRepository: CustomAttributesRepository,
) { ) {
private val appReceiver = object : BroadcastReceiver() { private val appReceiver = object : BroadcastReceiver() {
@ -93,36 +97,54 @@ class IconRepository(
fun getIcon(searchable: Searchable, size: Int): Flow<LauncherIcon> = channelFlow { fun getIcon(searchable: Searchable, size: Int): Flow<LauncherIcon> = channelFlow {
iconProviders.collectLatest { providers -> iconProviders.collectLatest { providers ->
transformations.collectLatest { transformations -> transformations.collectLatest { transformations ->
var icon = cache.get(searchable.key) customAttributesRepository.getCustomIcon(searchable).collectLatest { customIcon ->
if (icon != null) {
send(icon)
return@collectLatest
}
val placeholder = placeholderProvider?.getIcon(searchable, size) val transforms = getTransformations(customIcon) ?: transformations
placeholder?.let { send(it) }
for (provider in providers) { var icon = cache.get(searchable.key + customIcon.hashCode())
val ic = provider.getIcon(searchable, size) if (icon != null) {
if (ic != null) { send(icon)
icon = ic return@collectLatest
break
} }
}
if (icon != null) { val placeholder = placeholderProvider?.getIcon(searchable, size)
if (icon is StaticLauncherIcon) { placeholder?.let { send(it) }
for (transformation in transformations) {
icon = transformation.transform(icon as StaticLauncherIcon) for (provider in providers) {
val ic = provider.getIcon(searchable, size)
if (ic != null) {
icon = ic
break
} }
} }
if (icon != null) {
if (icon is StaticLauncherIcon) {
for (transformation in transforms) {
icon = transformation.transform(icon as StaticLauncherIcon)
}
}
cache.put(searchable.key, icon) cache.put(searchable.key + customIcon.hashCode(), icon)
send(icon) send(icon)
}
} }
} }
} }
} }
private fun getTransformations(customIcon: CustomIcon?): List<LauncherIconTransformation>? {
customIcon ?: return null
if (customIcon is AdaptifiedLegacyIcon) {
return listOf(
LegacyToAdaptiveTransformation(
foregroundScale = customIcon.fgScale,
backgroundColor = customIcon.bgColor
)
)
}
return null
}
fun requestIconPackListUpdate() { fun requestIconPackListUpdate() {
scope.launch { scope.launch {

View File

@ -5,5 +5,5 @@ import org.koin.dsl.module
val iconsModule = module { val iconsModule = module {
single { IconPackManager(androidContext()) } single { IconPackManager(androidContext()) }
single { IconRepository(androidContext(), get(), get()) } single { IconRepository(androidContext(), get(), get(), get()) }
} }

View File

@ -7,13 +7,16 @@ import de.mm20.launcher2.icons.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
internal class LegacyToAdaptiveTransformation: LauncherIconTransformation { internal class LegacyToAdaptiveTransformation(
private val foregroundScale: Float = 0.7f,
private val backgroundColor: Int = 1,
): LauncherIconTransformation {
override suspend fun transform(icon: StaticLauncherIcon): StaticLauncherIcon { override suspend fun transform(icon: StaticLauncherIcon): StaticLauncherIcon {
if (icon.backgroundLayer !is TransparentLayer) return icon if (icon.backgroundLayer !is TransparentLayer) return icon
val bgColor = extractColor(icon.foregroundLayer) val bgColor = if (backgroundColor == 1) extractColor(icon.foregroundLayer) else backgroundColor
return StaticLauncherIcon( return StaticLauncherIcon(
foregroundLayer = scale(icon.foregroundLayer, 0.7f), foregroundLayer = scale(icon.foregroundLayer, foregroundScale),
backgroundLayer = ColorLayer(bgColor) backgroundLayer = ColorLayer(bgColor)
) )
} }

View File

@ -60,7 +60,7 @@ dependencyResolutionManagement {
listOf("kotlin.stdlib", "kotlinx.coroutines.core", "kotlinx.coroutines.android") listOf("kotlin.stdlib", "kotlinx.coroutines.core", "kotlinx.coroutines.android")
) )
version("androidx.compose.compiler", "1.2.0") version("androidx.compose.compiler", "1.3.0-beta01")
alias("androidx.compose.runtime") alias("androidx.compose.runtime")
.to("androidx.compose.runtime", "runtime") .to("androidx.compose.runtime", "runtime")
.version("1.2.0-rc03") .version("1.2.0-rc03")