android: add coil and media3 cache foundation for media
Some checks are pending
CI / test (push) Has started running

This commit is contained in:
Codex
2026-03-09 15:35:28 +03:00
parent 43b772a394
commit ffa2205a30
5 changed files with 74 additions and 2 deletions

View File

@@ -353,3 +353,8 @@
- Implemented enqueue + optimistic behavior for `sendText`, `editMessage`, and `deleteMessage` on network failures. - Implemented enqueue + optimistic behavior for `sendText`, `editMessage`, and `deleteMessage` on network failures.
- Added automatic pending-action flush on chat sync/load-more and before new message operations. - Added automatic pending-action flush on chat sync/load-more and before new message operations.
- Kept non-network server failures as immediate errors (no queueing), while allowing offline continuation. - Kept non-network server failures as immediate errors (no queueing), while allowing offline continuation.
### Step 60 - Media cache foundation (Coil + Exo cache)
- Added global Coil image loader cache policy in `MessengerApplication` (memory + disk cache).
- Added Media3 `SimpleCache` singleton module for media stream/file caching foundation.
- Added Media3/Coil core dependencies and configured cache sizes for mobile usage.

View File

@@ -74,7 +74,11 @@ dependencies {
implementation("androidx.compose.ui:ui:1.7.6") implementation("androidx.compose.ui:ui:1.7.6")
implementation("androidx.compose.ui:ui-tooling-preview:1.7.6") implementation("androidx.compose.ui:ui-tooling-preview:1.7.6")
implementation("androidx.compose.material3:material3:1.3.1") implementation("androidx.compose.material3:material3:1.3.1")
implementation("io.coil-kt:coil:2.7.0")
implementation("io.coil-kt:coil-compose:2.7.0") implementation("io.coil-kt:coil-compose:2.7.0")
implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-datasource:1.4.1")
implementation("androidx.media3:media3-datasource-okhttp:1.4.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")

View File

@@ -1,13 +1,39 @@
package ru.daemonlord.messenger package ru.daemonlord.messenger
import android.app.Application import android.app.Application
import coil.ImageLoader
import coil.ImageLoaderFactory
import coil.disk.DiskCache
import coil.memory.MemoryCache
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import ru.daemonlord.messenger.core.notifications.NotificationChannels import ru.daemonlord.messenger.core.notifications.NotificationChannels
import java.io.File
@HiltAndroidApp @HiltAndroidApp
class MessengerApplication : Application() { class MessengerApplication : Application(), ImageLoaderFactory {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
NotificationChannels.ensureCreated(this) NotificationChannels.ensureCreated(this)
} }
override fun newImageLoader(): ImageLoader {
val diskCacheDir = File(cacheDir, "coil_images")
if (!diskCacheDir.exists()) {
diskCacheDir.mkdirs()
}
return ImageLoader.Builder(this)
.memoryCache {
MemoryCache.Builder(this)
.maxSizePercent(0.25)
.build()
}
.diskCache {
DiskCache.Builder()
.directory(diskCacheDir)
.maxSizeBytes(250L * 1024L * 1024L)
.build()
}
.respectCacheHeaders(false)
.build()
}
} }

View File

@@ -0,0 +1,37 @@
package ru.daemonlord.messenger.di
import android.content.Context
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.cache.Cache
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
import androidx.media3.datasource.cache.SimpleCache
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import java.io.File
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object MediaCacheModule {
@Provides
@Singleton
fun provideMediaCache(
@ApplicationContext context: Context,
): Cache {
val cacheDir = File(context.cacheDir, "exo_media_cache")
if (!cacheDir.exists()) {
cacheDir.mkdirs()
}
val maxBytes = 200L * 1024L * 1024L
return SimpleCache(
cacheDir,
LeastRecentlyUsedCacheEvictor(maxBytes),
StandaloneDatabaseProvider(context),
)
}
}

View File

@@ -20,7 +20,7 @@
## 3. Локальное хранение и sync ## 3. Локальное хранение и sync
- [x] Room для чатов/сообщений/пользователей - [x] Room для чатов/сообщений/пользователей
- [x] DataStore для настроек - [x] DataStore для настроек
- [ ] Кэш медиа (Coil/Exo cache) - [x] Кэш медиа (Coil/Exo cache)
- [x] Offline-first чтение истории - [x] Offline-first чтение истории
- [x] Очередь отложенных действий (send/edit/delete) - [x] Очередь отложенных действий (send/edit/delete)
- [x] Конфликт-резолв и reconcile после reconnect - [x] Конфликт-резолв и reconcile после reconnect