feat: (Tried) to deal with variable font

This commit is contained in:
Pun Butrach
2025-11-22 23:17:21 +07:00
parent 1aa5a1d90d
commit 963adfb629
3 changed files with 98 additions and 9 deletions
+1
View File
@@ -19,6 +19,7 @@ This is a developer-focused change log:
* Material 3 Expressive Settings (Phase 2, TODO)
* Better At-a-Glance perceptive wallpaper colour luminance detection
* Big word that means Lawnchair will take system default hint bright/dark theme and fallback to luminosity detection for bright/dark mode detection in At a Glance.
* Variable font for Launcher3 (????????)
### 🥞 Snapshot 9 (Development 4 Release 1)
@@ -0,0 +1,13 @@
package app.lawnchair.font
object FontAxes {
const val WEIGHT = "wght"
const val WIDTH = "wdth"
const val OPTICAL_SIZE = "opsz"
const val GRADE = "GRAD"
const val ROUNDNESS = "ROND"
fun mapToString(axes: Map<String, Float>): String {
return axes.entries.joinToString(", ") { "'${it.key}' ${it.value}" }
}
}
+84 -9
View File
@@ -22,6 +22,7 @@ import android.content.res.AssetManager
import android.graphics.Typeface
import android.net.Uri
import androidx.annotation.Keep
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.font.Font as ComposeFont
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
@@ -75,10 +76,49 @@ class FontCache @Inject constructor(
.toList()
}
val uiRegular = ResourceFont(context, R.font.googlesansflex_variable, "Google Sans Flex " + context.getString(R.string.font_weight_regular))
val uiMedium = ResourceFont(context, R.font.googlesansflex_variable, "Google Sans Flex " + context.getString(R.string.font_weight_medium))
val uiText = ResourceFont(context, R.font.googlesansflex_variable, "Google Sans Flex " + context.getString(R.string.font_weight_regular))
val uiTextMedium = ResourceFont(context, R.font.googlesansflex_variable, "Google Sans Flex " + context.getString(R.string.font_weight_medium))
val uiRegular = ResourceFont(
context,
R.font.googlesansflex_variable,
"Google Sans Flex " + context.getString(R.string.font_weight_medium),
mapOf(
FontAxes.WEIGHT to FontWeight.Normal.weight.toFloat(),
FontAxes.ROUNDNESS to 100f,
FontAxes.GRADE to 100f
)
)
val uiMedium = ResourceFont(
context,
R.font.googlesansflex_variable,
"Google Sans Flex " + context.getString(R.string.font_weight_medium),
mapOf(
FontAxes.WEIGHT to FontWeight.Medium.weight.toFloat(),
FontAxes.ROUNDNESS to 100f,
FontAxes.GRADE to 0f
)
)
val uiText = ResourceFont(
context,
R.font.googlesansflex_variable,
"Google Sans Flex " + context.getString(R.string.font_weight_medium),
mapOf(
FontAxes.WEIGHT to FontWeight.Normal.weight.toFloat(),
FontAxes.ROUNDNESS to 100f,
FontAxes.GRADE to 0f
)
)
val uiTextMedium = ResourceFont(
context,
R.font.googlesansflex_variable,
"Google Sans Flex " + context.getString(R.string.font_weight_medium),
mapOf(
FontAxes.WEIGHT to FontWeight.Medium.weight.toFloat(),
FontAxes.ROUNDNESS to 100f,
FontAxes.GRADE to 100f
)
)
suspend fun getTypeface(font: Font): Typeface? {
return loadFontAsync(font).await()?.typeface
@@ -354,21 +394,36 @@ class FontCache @Inject constructor(
context: Context,
private val resId: Int,
private val name: String,
) : TypefaceFont(ResourcesCompat.getFont(context, resId)) {
private val axisSettings: Map<String, Float> = emptyMap()
) : TypefaceFont(createTypeface(context, resId, axisSettings)) {
private val hashCode = "ResourceFont|$name".hashCode()
private val hashCode = "ResourceFont|$name|$axisSettings".hashCode()
override val fullDisplayName = name
override val composeFontFamily = FontFamily(ComposeFont(resId))
@OptIn(ExperimentalTextApi::class)
override val composeFontFamily = FontFamily(
// Don't let it fool you, removing qualifier name makes everything 10x worse
androidx.compose.ui.text.font.Font(
resId = resId,
variationSettings = androidx.compose.ui.text.font.FontVariation.Settings(
*axisSettings.map {
androidx.compose.ui.text.font.FontVariation.Setting(it.key, it.value)
}.toTypedArray()
)
)
)
override fun saveToJson(obj: JSONObject) {
super.saveToJson(obj)
obj.put(KEY_RESOURCE_ID, resId)
obj.put(KEY_FAMILY_NAME, name)
val axesObj = JSONObject()
axisSettings.forEach { (k, v) -> axesObj.put(k, v) }
obj.put("axes", axesObj)
}
override fun equals(other: Any?): Boolean {
return other is ResourceFont && name == other.name
return other is ResourceFont && name == other.name && axisSettings == other.axisSettings
}
override fun hashCode(): Int {
@@ -376,13 +431,33 @@ class FontCache @Inject constructor(
}
companion object {
private fun createTypeface(context: Context, resId: Int, axes: Map<String, Float>): Typeface? {
if (axes.isEmpty()) {
return ResourcesCompat.getFont(context, resId)
}
return try {
context.resources.openRawResourceFd(resId).use { pfd ->
Typeface.Builder(pfd.fileDescriptor)
.setFontVariationSettings(FontAxes.mapToString(axes))
.build()
}
} catch (e: Exception) {
ResourcesCompat.getFont(context, resId)
}
}
@Keep
@JvmStatic
fun fromJson(context: Context, obj: JSONObject): Font {
val resId = obj.getInt(KEY_RESOURCE_ID)
val name = obj.getString(KEY_FAMILY_NAME)
return ResourceFont(context, resId, name)
val axesMap = mutableMapOf<String, Float>()
val axesObj = obj.optJSONObject("axes")
axesObj?.keys()?.forEach { key ->
axesMap[key] = axesObj.getDouble(key).toFloat()
}
return ResourceFont(context, resId, name, axesMap)
}
}
}