Skip to content

Commit 3ce8e36

Browse files
committed
feat: Preload bundle avatars on startup to eliminate first-open delay
1 parent 05d9441 commit 3ce8e36

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

app/src/main/java/app/morphe/manager/ManagerApplication.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ import app.morphe.manager.di.*
1111
import app.morphe.manager.domain.manager.PreferencesManager
1212
import app.morphe.manager.domain.repository.PatchBundleRepository
1313
import app.morphe.manager.domain.repository.PatchBundleRepository.Companion.DEFAULT_SOURCE_UID
14+
import app.morphe.manager.domain.bundles.PatchBundleSource.Extensions.bundleAvatarUrl
15+
import app.morphe.manager.domain.bundles.PatchBundleSource.Extensions.githubAvatarUrl
16+
import app.morphe.manager.domain.bundles.PatchBundleSource.Extensions.gitlabAvatarUrl
1417
import app.morphe.manager.util.UpdateNotificationManager
1518
import app.morphe.manager.util.applyAppLanguage
19+
import app.morphe.manager.util.loadRemoteAvatar
1620
import app.morphe.manager.util.readLanguageFromPrefs
1721
import app.morphe.manager.util.saveLanguageToPrefs
1822
import app.morphe.manager.util.syncFcmTopics
@@ -25,6 +29,7 @@ import coil.ImageLoader
2529
import com.topjohnwu.superuser.Shell
2630
import kotlinx.coroutines.Dispatchers
2731
import kotlinx.coroutines.MainScope
32+
import kotlinx.coroutines.flow.first
2833
import kotlinx.coroutines.launch
2934
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
3035
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
@@ -124,6 +129,23 @@ class ManagerApplication : Application() {
124129
}
125130
}
126131

132+
// Preload bundle avatar images into AvatarCache while the user hasn't opened the sheet yet.
133+
// Suspends until sources are ready, then fetches all URLs in parallel on IO threads.
134+
scope.launch(Dispatchers.IO) {
135+
patchBundleRepository.sources.first { it.isNotEmpty() }.forEach { bundle ->
136+
launch {
137+
val primary = bundle.bundleAvatarUrl ?: bundle.githubAvatarUrl ?: bundle.gitlabAvatarUrl
138+
val fallback = when {
139+
bundle.bundleAvatarUrl != null -> bundle.githubAvatarUrl ?: bundle.gitlabAvatarUrl
140+
bundle.githubAvatarUrl != null -> bundle.gitlabAvatarUrl
141+
else -> null
142+
}
143+
primary?.let { loadRemoteAvatar(it) }
144+
fallback?.let { loadRemoteAvatar(it) }
145+
}
146+
}
147+
}
148+
127149
// Clean temp dir on fresh start
128150
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
129151
private var firstActivityCreated = false

app/src/main/java/app/morphe/manager/util/AvatarUtils.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ import androidx.compose.ui.layout.ContentScale
1515
import kotlinx.coroutines.Dispatchers
1616
import kotlinx.coroutines.withContext
1717
import java.net.URL
18+
import java.util.concurrent.ConcurrentHashMap
1819

1920
/**
2021
* In-memory avatar cache scoped to the process lifetime.
2122
*/
2223
object AvatarCache {
23-
private val cache = mutableMapOf<String, Bitmap>()
24+
private val cache = ConcurrentHashMap<String, Bitmap>()
2425

2526
operator fun get(url: String): Bitmap? = cache[url]
2627
operator fun set(url: String, bitmap: Bitmap) { cache[url] = bitmap }

0 commit comments

Comments
 (0)