Implement a cache for faster typeface resolution.

This commit is contained in:
Alex Hart
2022-04-06 13:12:28 -03:00
committed by Cody Henthorne
parent 46bb64ad24
commit 6fb6092a6b
10 changed files with 139 additions and 29 deletions

View File

@@ -8,7 +8,6 @@ import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.s3.S3
import org.thoughtcrime.securesms.util.ListenableFutureTask
import org.thoughtcrime.securesms.util.LocaleUtil
import java.io.File
import java.util.Collections
import java.util.Locale
@@ -52,12 +51,12 @@ object Fonts {
*
* @param context An application context
* @param font The desired font
* @param guessedScript The script likely being used based on text content
* @param supportedScript The script likely being used based on text content
*
* @return a FontResult that represents either a Typeface or a task retrieving a Typeface.
*/
@WorkerThread
fun resolveFont(context: Context, font: TextFont, guessedScript: SupportedScript = SupportedScript.UNKNOWN): FontResult {
fun resolveFont(context: Context, font: TextFont, supportedScript: SupportedScript): FontResult {
ThreadUtil.assertNotMainThread()
synchronized(this) {
val errorFallback = FontResult.Immediate(Typeface.create(font.fallbackFamily, font.fallbackStyle))
@@ -70,8 +69,6 @@ object Fonts {
Log.d(TAG, "Loaded manifest.")
val localeDefaults: List<Locale> = LocaleUtil.getLocaleDefaults()
val supportedScript: SupportedScript = getSupportedScript(localeDefaults, guessedScript)
val fontScript = resolveFontScriptFromScriptName(supportedScript, manifest)
if (fontScript == null) {
Log.d(TAG, "Manifest does not have an entry for $supportedScript. Using default.")
@@ -253,7 +250,7 @@ object Fonts {
}
}
private fun getSupportedScript(locales: List<Locale>, guessedScript: SupportedScript): SupportedScript {
fun getSupportedScript(locales: List<Locale>, guessedScript: SupportedScript): SupportedScript {
if (guessedScript != SupportedScript.UNKNOWN && guessedScript != SupportedScript.UNKNOWN_CJK) {
return guessedScript
} else if (guessedScript == SupportedScript.UNKNOWN_CJK) {

View File

@@ -0,0 +1,72 @@
package org.thoughtcrime.securesms.fonts
import android.content.Context
import android.graphics.Typeface
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.thoughtcrime.securesms.util.FutureTaskListener
import org.thoughtcrime.securesms.util.LocaleUtil
import java.util.Collections
import java.util.concurrent.ExecutionException
/**
* In-Memory Typeface cache
*/
object TypefaceCache {
private val cache = Collections.synchronizedMap(mutableMapOf<CacheKey, Typeface>())
/**
* Warms the typeface-cache with all fonts of a given script.
*/
fun warm(context: Context, script: SupportedScript) {
val appContext = context.applicationContext
TextFont.values().forEach {
get(appContext, it, script).subscribe()
}
}
/**
* Grabs the font and caches it on the fly.
*/
fun get(context: Context, font: TextFont, guessedScript: SupportedScript = SupportedScript.UNKNOWN): Single<Typeface> {
val supportedScript = Fonts.getSupportedScript(LocaleUtil.getLocaleDefaults(), guessedScript)
val cacheKey = CacheKey(supportedScript, font)
val cachedValue = cache[cacheKey]
val appContext = context.applicationContext
if (cachedValue != null) {
return Single.just(cachedValue)
} else {
return Single.create<Typeface> { emitter ->
when (val result = Fonts.resolveFont(appContext, font, supportedScript)) {
is Fonts.FontResult.Immediate -> {
cache[cacheKey] = result.typeface
emitter.onSuccess(result.typeface)
}
is Fonts.FontResult.Async -> {
val listener = object : FutureTaskListener<Typeface> {
override fun onSuccess(typeface: Typeface) {
cache[cacheKey] = typeface
emitter.onSuccess(typeface)
}
override fun onFailure(exception: ExecutionException) {
emitter.onSuccess(result.placeholder)
}
}
result.future.addListener(listener)
emitter.setCancellable {
result.future.removeListener(listener)
}
}
}
}.subscribeOn(Schedulers.io())
}
}
private data class CacheKey(
val script: SupportedScript,
val font: TextFont
)
}