Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6ce0a46
Introduce MediaModel local cache
wzieba Jan 14, 2026
2896ba0
Replace `MediaSqlUtils` with `MediaCacheOperations` that uses in-memo…
wzieba Jan 15, 2026
efabd2e
Remove `mLocalPostId` from `MediaModel`
wzieba Jan 15, 2026
bdbcb08
Remove `MediaFields` from `MediaModel`
wzieba Jan 15, 2026
cbd1684
Remove unused constructors from `MediaModel`
wzieba Jan 15, 2026
64deb8d
Remove `MediaSqlUtils`
wzieba Jan 15, 2026
9f35c48
Remove unused `MediaModel` constructor
wzieba Jan 15, 2026
b8ea0bc
Remove any mentions of WellSql from `MediaModel` and unused WellSql c…
wzieba Jan 16, 2026
dabae37
Remove all unused setters for `MediaModel`
wzieba Jan 16, 2026
db0b3f8
Rename `MediaFileRepository#fetchMedia` to `getLocalMedia`
wzieba Jan 19, 2026
9738010
Remove unused methods/obsolete comments
wzieba Jan 19, 2026
cfe92ff
Remove another set of unused `MediaModel` properties
wzieba Jan 19, 2026
521e2e4
Remove `MediaModel#mUploadState` property
wzieba Jan 20, 2026
a7809f6
Add WellSql migration to remove `MediaModel` table
wzieba Jan 20, 2026
2bac569
`MediaLibraryCacheTest` uses `MediaTestUtils#createRemoteTestMedia()`…
wzieba Jan 21, 2026
32d4ec0
Remove `MediaUploadState` enum
wzieba Jan 21, 2026
bc5c923
Add `MediaModel#hashCode`
wzieba Jan 21, 2026
25bf86f
Adjust name of `MediaLibraryCacheTest` to reflect correct (remote) id
wzieba Jan 21, 2026
fb896cb
Make `MediaStore#instantiateMediaModel` `@NonNull`
wzieba Jan 21, 2026
307f316
Add test for race condition in MediaLibraryCache.addOrUpdate()
wzieba Jan 21, 2026
8ba7445
Use `ConcurrentHashMap#compute`
wzieba Jan 21, 2026
c92a68d
Apply visibility modifiers to `Media*` classes
wzieba Jan 23, 2026
38dfa49
Make `MediaCacheOperations#getCacheSize` private
wzieba Jan 23, 2026
6ebd22e
Remove `MediaLibraryCache#clear`
wzieba Jan 23, 2026
3f95039
Change formatting of `MediaModel#hashCode`
wzieba Jan 23, 2026
f60ab06
Change formatting of `MediaLibraryCacheTest#createTestMedia`
wzieba Jan 23, 2026
f10272b
Rename `MediaLibraryCache` -> `RemoteMediaCache`
wzieba Jan 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.wordpress.android.fluxc.di.WCDatabaseModule
import org.wordpress.android.fluxc.module.MediaModule
import org.wordpress.android.fluxc.module.OkHttpClientModule
import org.wordpress.android.fluxc.module.ReleaseNetworkModule

Expand All @@ -12,7 +13,8 @@ import org.wordpress.android.fluxc.module.ReleaseNetworkModule
includes = [
ReleaseNetworkModule::class,
OkHttpClientModule::class,
WCDatabaseModule::class
WCDatabaseModule::class,
MediaModule::class
]
)
abstract class FluxCModule
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.wordpress.android.fluxc.di.WCDatabaseModule
import org.wordpress.android.fluxc.module.MediaModule
import org.wordpress.android.fluxc.module.OkHttpClientModule
import org.wordpress.android.fluxc.module.ReleaseNetworkModule

Expand All @@ -12,7 +13,8 @@ import org.wordpress.android.fluxc.module.ReleaseNetworkModule
includes = [
ReleaseNetworkModule::class,
OkHttpClientModule::class,
WCDatabaseModule::class
WCDatabaseModule::class,
MediaModule::class
]
)
abstract class FluxCModule
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,9 @@ object FileUploadUtils {
path,
mimeType,
filenameWithExtension,
null
)
val instantiatedMedia = mediaStore.instantiateMediaModel(media)
return if (instantiatedMedia != null) {
instantiatedMedia
} else {
WooLog.w(T.MEDIA, "We couldn't instantiate the media")
null
}
return instantiatedMedia
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class MediaFilesRepository @Inject constructor(
private val resourceProvider: ResourceProvider,
private val mediaPickerUtils: MediaPickerUtils
) {
suspend fun fetchMedia(localUri: String): MediaModel? {
suspend fun getLocalMedia(localUri: String): MediaModel? {
return withContext(dispatchers.io) {
val mediaModel = FileUploadUtils.mediaModelFromLocalUri(
context,
Expand Down Expand Up @@ -120,7 +120,7 @@ class MediaFilesRepository @Inject constructor(

fun uploadFile(localUri: String): Flow<UploadResult> {
return flow {
val mediaModel = fetchMedia(localUri)
val mediaModel = getLocalMedia(localUri)

if (mediaModel == null) {
WooLog.w(T.MEDIA, "MediaFilesRepository > null media")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package com.woocommerce.android.media

import com.woocommerce.android.di.AppCoroutineScope
import com.woocommerce.android.media.MediaFilesRepository.UploadResult.*
import com.woocommerce.android.media.ProductImagesUploadWorker.Event
import com.woocommerce.android.media.ProductImagesUploadWorker.Event.MediaUploadEvent
import com.woocommerce.android.media.ProductImagesUploadWorker.Work
import com.woocommerce.android.model.Product
import com.woocommerce.android.model.toAppModel
import com.woocommerce.android.ui.products.details.ProductDetailRepository
Expand Down Expand Up @@ -172,7 +170,7 @@ class ProductImagesUploadWorker @Inject constructor(
private suspend fun fetchMedia(work: Work.FetchMedia) {
WooLog.d(T.MEDIA, "ProductImagesUploadWorker -> fetch media ${work.localUri}")

val fetchedMedia = mediaFilesRepository.fetchMedia(work.localUri)
val fetchedMedia = mediaFilesRepository.getLocalMedia(work.localUri)
if (fetchedMedia == null) {
WooLog.w(T.MEDIA, "ProductImagesUploadWorker -> fetching media failed")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.wordpress.android.fluxc.model.MediaModel
import org.wordpress.android.fluxc.media.MediaTestUtils
import org.wordpress.android.fluxc.store.MediaStore.MediaErrorType.GENERIC_ERROR
import org.wordpress.android.util.DateTimeUtils
import java.util.Date
Expand All @@ -45,20 +45,19 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() {
companion object {
private const val REMOTE_PRODUCT_ID = 1L
private const val TEST_URI = "test"
private val FETCHED_MEDIA = MediaModel(0, 0)
private val UPLOADED_MEDIA = MediaModel(0, 0).apply {
fileName = ""
filePath = ""
url = ""
uploadDate = DateTimeUtils.iso8601FromDate(Date())
}
private val FETCHED_MEDIA = MediaTestUtils.createRemoteTestMedia().build()
private val UPLOADED_MEDIA = MediaTestUtils.createRemoteTestMedia()
.fileName("")
.url("")
.uploadDate(DateTimeUtils.iso8601FromDate(Date()))
.build()
}

private val notificationHandler: ProductImagesNotificationHandler = mock()
private val productImagesServiceWrapper: ProductImagesServiceWrapper = mock()
private lateinit var worker: ProductImagesUploadWorker
private val mediaFilesRepository: MediaFilesRepository = mock {
onBlocking { fetchMedia(TEST_URI) } doReturn FETCHED_MEDIA
onBlocking { getLocalMedia(TEST_URI) } doReturn FETCHED_MEDIA
onBlocking { uploadMedia(any(), any()) } doReturn flowOf(UploadResult.UploadSuccess(UPLOADED_MEDIA))
}
private val productDetailRepository: ProductDetailRepository = mock()
Expand Down Expand Up @@ -111,7 +110,7 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() {
}
worker.enqueueWork(Work.FetchMedia(REMOTE_PRODUCT_ID, TEST_URI))

verify(mediaFilesRepository).fetchMedia(TEST_URI)
verify(mediaFilesRepository).getLocalMedia(TEST_URI)
assertThat(eventsList[0]).isEqualTo(FetchSucceeded(REMOTE_PRODUCT_ID, TEST_URI, FETCHED_MEDIA))
job.cancel()
}
Expand All @@ -122,7 +121,9 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() {
val job = launch {
worker.events.toList(eventsList)
}
worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaModel(0, 0)))
worker.enqueueWork(
Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build())
)

advanceUntilIdle()
verify(mediaFilesRepository).uploadMedia(any(), any())
Expand All @@ -133,9 +134,11 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() {
@Test
fun `when media upload progress changes, then update notification`() = testBlocking {
whenever(mediaFilesRepository.uploadMedia(any(), any()))
.thenReturn(flowOf(UploadProgress(0.5f), UploadSuccess(MediaModel(0, 0))))
.thenReturn(flowOf(UploadProgress(0.5f), UploadSuccess(MediaTestUtils.createRemoteTestMedia().build())))

worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaModel(0, 0)))
worker.enqueueWork(
Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build())
)
advanceUntilIdle()

verify(notificationHandler).setProgress(0.5f)
Expand All @@ -153,7 +156,9 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() {
val job = launch {
worker.events.toList(eventsList)
}
worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaModel(0, 0)))
worker.enqueueWork(
Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build())
)

advanceUntilIdle()
assertThat(eventsList).contains(UploadFailed(REMOTE_PRODUCT_ID, TEST_URI, error))
Expand All @@ -166,7 +171,9 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() {
val job = launch {
worker.events.toList(eventsList)
}
worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaModel(0, 0)))
worker.enqueueWork(
Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build())
)

advanceUntilIdle()
assertThat(eventsList).contains(ProductUploadsCompleted(REMOTE_PRODUCT_ID))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.wordpress.android.fluxc.model.MediaModel
import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.FAILED
import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.UPLOADED
import org.wordpress.android.fluxc.media.MediaTestUtils
import org.wordpress.android.fluxc.store.MediaStore.MediaErrorType
import org.wordpress.android.fluxc.store.MediaStore.MediaErrorType.NULL_MEDIA_ARG
import org.wordpress.android.util.DateTimeUtils
Expand Down Expand Up @@ -80,7 +78,7 @@ class MediaFileUploadHandlerTest : BaseUnitTest() {
fun `when media is fetched, then start uploading it`() = testBlocking {
mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI))

val fetchedMedia = MediaModel(0, 0)
val fetchedMedia = MediaTestUtils.createRemoteTestMedia().build()
eventsFlow.tryEmit(
Event.MediaUploadEvent.FetchSucceeded(
REMOTE_PRODUCT_ID,
Expand Down Expand Up @@ -125,14 +123,12 @@ class MediaFileUploadHandlerTest : BaseUnitTest() {
mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI))

launch {
val successfulUpload = mediaFileUploadHandler.observeSuccessfulUploads(REMOTE_PRODUCT_ID).first()
assertThat(successfulUpload.uploadState).isEqualTo(UPLOADED.toString())
mediaFileUploadHandler.observeSuccessfulUploads(REMOTE_PRODUCT_ID).first()
}

val mediaModel = MediaModel(0, 0).apply {
postId = REMOTE_PRODUCT_ID
setUploadState(UPLOADED)
}
val mediaModel = MediaTestUtils.createRemoteTestMedia()
.postId(REMOTE_PRODUCT_ID)
.build()
eventsFlow.tryEmit(
Event.MediaUploadEvent.UploadSucceeded(
REMOTE_PRODUCT_ID,
Expand All @@ -146,13 +142,12 @@ class MediaFileUploadHandlerTest : BaseUnitTest() {
fun `given there is no external observer, when uploads finish, then start product update`() = testBlocking {
mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI))

val mediaModel = MediaModel(0, 0).apply {
postId = REMOTE_PRODUCT_ID
fileName = "test"
url = "url"
uploadDate = DateTimeUtils.iso8601FromDate(Date())
setUploadState(UPLOADED)
}
val mediaModel = MediaTestUtils.createRemoteTestMedia()
.fileName("test")
.url("url")
.uploadDate(DateTimeUtils.iso8601FromDate(Date()))
.postId(REMOTE_PRODUCT_ID)
.build()
eventsFlow.tryEmit(
Event.MediaUploadEvent.UploadSucceeded(
REMOTE_PRODUCT_ID,
Expand All @@ -170,13 +165,12 @@ class MediaFileUploadHandlerTest : BaseUnitTest() {
val testUri2 = "file:///test2"
mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI, testUri2))

val mediaModel = MediaModel(0, 0).apply {
postId = REMOTE_PRODUCT_ID
fileName = "test"
url = "url"
uploadDate = DateTimeUtils.iso8601FromDate(Date())
setUploadState(UPLOADED)
}
val mediaModel = MediaTestUtils.createRemoteTestMedia()
.fileName("test")
.url("url")
.uploadDate(DateTimeUtils.iso8601FromDate(Date()))
.postId(REMOTE_PRODUCT_ID)
.build()
eventsFlow.tryEmit(
Event.MediaUploadEvent.UploadSucceeded(
REMOTE_PRODUCT_ID,
Expand All @@ -203,10 +197,9 @@ class MediaFileUploadHandlerTest : BaseUnitTest() {

val job = launch { mediaFileUploadHandler.observeSuccessfulUploads(REMOTE_PRODUCT_ID).collect() }

val mediaModel = MediaModel(0, 0).apply {
postId = REMOTE_PRODUCT_ID
setUploadState(FAILED)
}
val mediaModel = MediaTestUtils.createRemoteTestMedia()
.postId(REMOTE_PRODUCT_ID)
.build()

eventsFlow.tryEmit(
Event.MediaUploadEvent.UploadFailed(
Expand All @@ -229,10 +222,9 @@ class MediaFileUploadHandlerTest : BaseUnitTest() {
fun `given there is no external observer, when an upload fails, then show notification`() = testBlocking {
mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI))

val mediaModel = MediaModel(0, 0).apply {
postId = REMOTE_PRODUCT_ID
setUploadState(FAILED)
}
val mediaModel = MediaTestUtils.createRemoteTestMedia()
.postId(REMOTE_PRODUCT_ID)
.build()
eventsFlow.tryEmit(
Event.MediaUploadEvent.UploadFailed(
REMOTE_PRODUCT_ID,
Expand Down Expand Up @@ -261,12 +253,11 @@ class MediaFileUploadHandlerTest : BaseUnitTest() {
@Test
fun `when assigning uploads to created product, then update the id for the successful ones`() = testBlocking {
mediaFileUploadHandler.enqueueUpload(ProductDetailViewModel.DEFAULT_ADD_NEW_PRODUCT_ID, listOf(TEST_URI))
val mediaModel = MediaModel(0, 0).apply {
fileName = "test"
url = "url"
uploadDate = DateTimeUtils.iso8601FromDate(Date())
setUploadState(UPLOADED)
}
val mediaModel = MediaTestUtils.createRemoteTestMedia()
.fileName("test")
.url("url")
.uploadDate(DateTimeUtils.iso8601FromDate(Date()))
.build()
eventsFlow.tryEmit(
Event.MediaUploadEvent.UploadSucceeded(
ProductDetailViewModel.DEFAULT_ADD_NEW_PRODUCT_ID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.wordpress.android.fluxc.model.MediaModel
import org.wordpress.android.fluxc.media.MediaTestUtils
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.store.MediaStore
import org.wordpress.android.fluxc.store.WCProductStore
Expand Down Expand Up @@ -1090,10 +1090,10 @@ class ProductDetailViewModelTest : BaseUnitTest() {
on { it.observeCurrentUploadErrors(any()) } doReturn emptyFlow()
on { it.observeCurrentUploads(any()) } doReturn flowOf(emptyList())
on { it.observeSuccessfulUploads(any()) } doReturn uris.map {
MediaModel(0, 0).apply {
url = it
uploadDate = "2022-09-27 18:00:00.000"
}
MediaTestUtils.createRemoteTestMedia()
.url(it)
.uploadDate("2022-09-27 18:00:00.000")
.build()
}.asFlow()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
package org.wordpress.android.fluxc.model

import com.google.gson.JsonObject
import org.wordpress.android.fluxc.utils.DateUtils

class WCProductImageModel(val id: Long) {
var dateCreated: String = ""
var src: String = ""
var alt: String = ""
var name: String = ""

companion object {
fun fromMediaModel(media: MediaModel): WCProductImageModel {
with(WCProductImageModel(media.mediaId)) {
dateCreated = media.uploadDate ?: DateUtils.getCurrentDateString()
src = media.url
alt = media.alt
name = media.fileName ?: ""
return this
}
}
}

fun toJson(): JsonObject {
return JsonObject().also { json ->
json.addProperty("id", id)
Expand Down
Loading
Loading