From 6ce0a46e6d1ee26227bfe29e4267db6f502d3755 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 14 Jan 2026 16:40:41 +0100 Subject: [PATCH 01/27] Introduce MediaModel local cache We don't need these to be stored in a database. More internally: paaHJt-9qo-p2 `MediaIdGenerator` will be necessary for a temporary id during the media upload --- .../android/wear/di/FluxCModule.kt | 4 +- .../com/woocommerce/android/di/FluxCModule.kt | 4 +- .../android/fluxc/module/MediaModule.kt | 21 +++ .../android/fluxc/store/MediaIdGenerator.kt | 7 + .../android/fluxc/store/MediaLibraryCache.kt | 40 +++++ .../fluxc/store/TimestampMediaIdGenerator.kt | 14 ++ .../fluxc/store/MediaLibraryCacheTest.kt | 157 ++++++++++++++++++ .../store/TimestampMediaIdGeneratorTest.kt | 52 ++++++ 8 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt create mode 100644 libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt create mode 100644 libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt diff --git a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/di/FluxCModule.kt b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/di/FluxCModule.kt index 83958ee1d05b..d6bc70de05c0 100644 --- a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/di/FluxCModule.kt +++ b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/di/FluxCModule.kt @@ -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 @@ -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 diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/di/FluxCModule.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/di/FluxCModule.kt index 9d305391e0c1..a84f81c9d811 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/di/FluxCModule.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/di/FluxCModule.kt @@ -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 @@ -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 diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt new file mode 100644 index 000000000000..31767a6fd6c8 --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt @@ -0,0 +1,21 @@ +package org.wordpress.android.fluxc.module + +import dagger.Binds +import dagger.Module +import dagger.Provides +import org.wordpress.android.fluxc.store.MediaIdGenerator +import org.wordpress.android.fluxc.store.TimestampMediaIdGenerator +import kotlin.time.Clock +import kotlin.time.ExperimentalTime + +@OptIn(ExperimentalTime::class) +@Module +interface MediaModule { + @Binds + fun bindMediaIdGenerator(generator: TimestampMediaIdGenerator): MediaIdGenerator + + companion object { + @Provides + fun provideClock(): Clock = Clock.System + } +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt new file mode 100644 index 000000000000..6b40b7f1d753 --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt @@ -0,0 +1,7 @@ +package org.wordpress.android.fluxc.store + +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId + +interface MediaIdGenerator { + fun generate(filePath: String): LocalId +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt new file mode 100644 index 000000000000..b27b60f6900c --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt @@ -0,0 +1,40 @@ +package org.wordpress.android.fluxc.store + +import org.wordpress.android.fluxc.model.MediaModel +import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MediaLibraryCache @Inject constructor() { + private val cache = ConcurrentHashMap>() + + fun getMediaList(localSiteId: Int): List? { + return cache[localSiteId] + } + + fun cacheMediaList(localSiteId: Int, mediaList: List) { + cache[localSiteId] = mediaList + } + + fun addOrUpdate(localSiteId: Int, media: MediaModel) { + val currentList = cache[localSiteId] ?: emptyList() + val mutableList = currentList.toMutableList() + val existingIndex = mutableList.indexOfFirst { it.mediaId == media.mediaId } + if (existingIndex != -1) { + mutableList[existingIndex] = media + } else { + mutableList.add(media) + } + cache[localSiteId] = mutableList + } + + fun remove(localSiteId: Int, mediaId: Long) { + val currentList = cache[localSiteId] ?: return + cache[localSiteId] = currentList.filter { it.mediaId != mediaId } + } + + fun clear() { + cache.clear() + } +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt new file mode 100644 index 000000000000..f15d433a55b2 --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt @@ -0,0 +1,14 @@ +package org.wordpress.android.fluxc.store + +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId +import javax.inject.Inject +import kotlin.time.Clock +import kotlin.time.ExperimentalTime + +@OptIn(ExperimentalTime::class) +class TimestampMediaIdGenerator @Inject constructor(private val clock: Clock) : MediaIdGenerator { + override fun generate(filePath: String): LocalId { + val combined = "$filePath:${clock.now().toEpochMilliseconds()}" + return LocalId(combined.hashCode()) + } +} diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt new file mode 100644 index 000000000000..7de4d6474a62 --- /dev/null +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -0,0 +1,157 @@ +package org.wordpress.android.fluxc.store + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.wordpress.android.fluxc.model.MediaModel + +class MediaLibraryCacheTest { + private lateinit var cache: MediaLibraryCache + private lateinit var testMedia1: MediaModel + private lateinit var testMedia2: MediaModel + + @Before + fun setup() { + cache = MediaLibraryCache() + testMedia1 = createTestMedia(1, "image1.jpg") + testMedia2 = createTestMedia(2, "image2.jpg") + } + + @Test + fun `when getting media list for uncached site, then it returns null`() { + val result = cache.getMediaList(1) + assertThat(result).isNull() + } + + @Test + fun `when caching media list, then it stores and retrieves correctly`() { + val mediaList = listOf(testMedia1, testMedia2) + + cache.cacheMediaList(1, mediaList) + val result = cache.getMediaList(1) + + assertThat(result).isEqualTo(mediaList) + } + + @Test + fun `when caching for different sites, then it stores different results`() { + val list1 = listOf(testMedia1) + val list2 = listOf(testMedia2) + + cache.cacheMediaList(1, list1) + cache.cacheMediaList(2, list2) + + assertThat(cache.getMediaList(1)).isEqualTo(list1) + assertThat(cache.getMediaList(2)).isEqualTo(list2) + } + + @Test + fun `when clearing, then it removes all entries`() { + cache.cacheMediaList(1, listOf(testMedia1)) + cache.cacheMediaList(2, listOf(testMedia2)) + + cache.clear() + + assertThat(cache.getMediaList(1)).isNull() + assertThat(cache.getMediaList(2)).isNull() + } + + @Test + fun `when caching media list, then it overwrites previous value for same site`() { + val list1 = listOf(testMedia1) + val list2 = listOf(testMedia2) + + cache.cacheMediaList(1, list1) + cache.cacheMediaList(1, list2) + + val result = cache.getMediaList(1) + assertThat(result).isEqualTo(list2) + } + + @Test + fun `when caching empty list, then it stores correctly`() { + cache.cacheMediaList(1, emptyList()) + + val result = cache.getMediaList(1) + assertThat(result).isEmpty() + } + + @Test + fun `when caching large list, then it stores correctly`() { + val largeList = (1..100).map { createTestMedia(it, "image$it.jpg") } + + cache.cacheMediaList(1, largeList) + val result = cache.getMediaList(1) + + assertThat(result).isEqualTo(largeList) + } + + @Test + fun `when adding new media, then it appends to list`() { + val existingList = listOf(testMedia1) + cache.cacheMediaList(1, existingList) + + cache.addOrUpdate(1, testMedia2) + + val result = cache.getMediaList(1) + assertThat(result).containsExactly(testMedia1, testMedia2) + } + + @Test + fun `when updating existing media by media id, then it replaces media`() { + val siteId = 123 + val original = createTestMedia(1, "original.jpg") + val updated = createTestMedia(1, "updated.jpg") + cache.cacheMediaList(siteId, listOf(original)) + + cache.addOrUpdate(siteId, updated) + + val result = cache.getMediaList(siteId) + assertThat(result).containsExactly(updated) + } + + @Test + fun `when adding to empty cache, then it creates new list`() { + cache.addOrUpdate(1, testMedia1) + + val result = cache.getMediaList(1) + assertThat(result).containsExactly(testMedia1) + } + + @Test + fun `when removing media by local id, then it filters out media`() { + val mediaToRemove = testMedia1 + cache.cacheMediaList(1, listOf(mediaToRemove, testMedia2)) + + cache.remove(1, mediaToRemove.mediaId) + + val result = cache.getMediaList(1) + assertThat(result).containsExactly(testMedia2) + } + + @Test + fun `when removing from empty cache, then it does nothing`() { + cache.remove(1, 2) + + val result = cache.getMediaList(1) + assertThat(result).isNull() + } + + private fun createTestMedia(id: Int, fileName: String): MediaModel { + return MediaModel( + 1, // localSiteId + id.toLong(), // mediaId + 0L, // postId + null, // uploadDate + "https://example.com/$fileName", // url + null, // thumbnailUrl + fileName, // fileName + "image/jpeg", // mimeType + fileName, // title + "", // caption + "", // description + "", // alt + MediaModel.MediaUploadState.UPLOADED // uploadState + ) + } +} diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt new file mode 100644 index 000000000000..d3597c23350d --- /dev/null +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt @@ -0,0 +1,52 @@ +package org.wordpress.android.fluxc.store + +import org.assertj.core.api.Assertions +import org.junit.Before +import org.junit.Test +import kotlin.time.Clock +import kotlin.time.ExperimentalTime +import kotlin.time.Instant + +@OptIn(ExperimentalTime::class) +class TimestampMediaIdGeneratorTest { + + lateinit var sut: TimestampMediaIdGenerator + + var currentMillis = 123L + private val fakeClock = object : Clock { + override fun now(): Instant { + return Instant.fromEpochMilliseconds(currentMillis) + } + } + + @Before + fun setUp() { + sut = TimestampMediaIdGenerator(fakeClock) + } + + @Test + fun `when generating with same inputs, then it returns consistent ID`() { + val id1 = sut.generate("/path/to/file.jpg") + val id2 = sut.generate("/path/to/file.jpg") + + Assertions.assertThat(id1).isEqualTo(id2) + } + + @Test + fun `when generating with different file paths, then it returns different IDs`() { + val id1 = sut.generate("/path/to/file1.jpg") + val id2 = sut.generate("/path/to/file2.jpg") + + Assertions.assertThat(id1).isNotEqualTo(id2) + } + + @Test + fun `when generating with different timestamps, then it returns different IDs`() { + currentMillis = 1000L + val id1 = sut.generate("/path/to/file.jpg") + currentMillis = 2000L + val id2 = sut.generate("/path/to/file.jpg") + + Assertions.assertThat(id1).isNotEqualTo(id2) + } +} From 2896ba0f1c2d266b86d1bb4a1c12c0c80cc7630c Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 15 Jan 2026 10:50:19 +0100 Subject: [PATCH 02/27] Replace `MediaSqlUtils` with `MediaCacheOperations` that uses in-memory cache `MediaCacheOperations` is created only to delegate as much as possible to a Kotlin class instead of modifying `MediaStore.java` --- .../android/fluxc/media/MediaStoreTest.java | 128 ++++++++++-------- .../android/fluxc/media/MediaTestUtils.java | 70 ++-------- .../fluxc/store/MediaCacheOperations.kt | 78 +++++++++++ .../android/fluxc/store/MediaStore.java | 113 ++++++---------- 4 files changed, 199 insertions(+), 190 deletions(-) create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt diff --git a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java index 02894352d72b..0eb9a527b8c8 100644 --- a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java +++ b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java @@ -4,12 +4,13 @@ import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.wordpress.android.fluxc.media.MediaTestUtils.generateMediaFromPath; -import static org.wordpress.android.fluxc.media.MediaTestUtils.insertMediaIntoDatabase; +import static org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId; import android.content.Context; import com.yarolegovich.wellsql.WellSql; +import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -18,26 +19,43 @@ import org.robolectric.RuntimeEnvironment; import org.wordpress.android.fluxc.Dispatcher; import org.wordpress.android.fluxc.SingleStoreWellSqlConfigForTests; +import org.wordpress.android.fluxc.logging.FakeCrashLogging; import org.wordpress.android.fluxc.model.MediaModel; import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.network.rest.wpapi.media.ApplicationPasswordsMediaRestClient; import org.wordpress.android.fluxc.network.rest.wpcom.media.wpv2.WPComV2MediaRestClient; import org.wordpress.android.fluxc.persistence.WellSqlConfig; +import org.wordpress.android.fluxc.store.MediaCacheOperations; +import org.wordpress.android.fluxc.store.MediaIdGenerator; +import org.wordpress.android.fluxc.store.MediaLibraryCache; import org.wordpress.android.fluxc.store.MediaStore; import org.wordpress.android.fluxc.utils.MediaUtils; -import org.wordpress.android.fluxc.logging.FakeCrashLogging; import java.util.List; @RunWith(RobolectricTestRunner.class) public class MediaStoreTest { + private final MediaLibraryCache mMediaLibraryCache = new MediaLibraryCache(); + private final MediaCacheOperations mMediaCacheOperations = new MediaCacheOperations(mMediaLibraryCache); + + private static class FakeMediaIdGenerator implements MediaIdGenerator { + private int nextId = 1; + @Override + public @NotNull LocalId generate(@NotNull String filePath) { + return new LocalId(nextId++); + } + } + @SuppressWarnings("KotlinInternalInJava") private final MediaStore mMediaStore = new MediaStore(new Dispatcher(), Mockito.mock(WPComV2MediaRestClient.class), Mockito.mock(ApplicationPasswordsMediaRestClient.class), Mockito.mock(org.wordpress.android.fluxc.network.rest.wpapi.applicationpasswords .ApplicationPasswordsConfiguration.class), - FakeCrashLogging.INSTANCE + FakeCrashLogging.INSTANCE, + mMediaLibraryCache, + mMediaCacheOperations, + new FakeMediaIdGenerator() ); @Before @@ -61,8 +79,8 @@ public void testGetSiteImages() { assertTrue(MediaUtils.isVideoMimeType(videoMedia.getMimeType())); MediaModel imageMedia = generateMediaFromPath(testSiteId, testImageId, testImagePath); assertTrue(MediaUtils.isImageMimeType(imageMedia.getMimeType())); - insertMediaIntoDatabase(videoMedia); - insertMediaIntoDatabase(imageMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, imageMedia); final List storeImages = mMediaStore.getSiteImages(getTestSiteWithLocalId(testSiteId)); assertNotNull(storeImages); @@ -83,24 +101,22 @@ public void testSearchSiteImages() { final long testAudioId = 540; // generate media of different types - MediaModel imageMedia = generateMediaFromPath(testSiteId, testImageId, testImagePath); - imageMedia.setTitle("Awesome Image"); - imageMedia.setDescription("This is an image test"); + MediaModel imageMedia = generateMediaFromPath(testSiteId, testImageId, testImagePath, + "Awesome Image", "This is an image test", null); assertTrue(MediaUtils.isImageMimeType(imageMedia.getMimeType())); - MediaModel videoMedia = generateMediaFromPath(testSiteId, testVideoId, testVideoPath); - videoMedia.setTitle("Video Title"); - videoMedia.setCaption("Test Caption"); + MediaModel videoMedia = generateMediaFromPath(testSiteId, testVideoId, testVideoPath, + "Video Title", null, "Test Caption"); assertTrue(MediaUtils.isVideoMimeType(videoMedia.getMimeType())); - MediaModel audioMedia = generateMediaFromPath(testSiteId, testAudioId, testAudioPath); - audioMedia.setDescription("This is an audio test"); + MediaModel audioMedia = generateMediaFromPath(testSiteId, testAudioId, testAudioPath, + null, "This is an audio test", null); assertTrue(MediaUtils.isAudioMimeType(audioMedia.getMimeType())); // insert media of different types - insertMediaIntoDatabase(videoMedia); - insertMediaIntoDatabase(imageMedia); - insertMediaIntoDatabase(audioMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, imageMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia); // verify the correct media is returned final List storeImages = mMediaStore @@ -125,22 +141,22 @@ public void testSearchSiteVideos() { final long testDocumentId = 125; // generate media of different types - MediaModel videoMedia1 = generateMediaFromPath(testSiteId, testVideoId1, testVideoPath1); - videoMedia1.setTitle("My trip title"); + MediaModel videoMedia1 = generateMediaFromPath(testSiteId, testVideoId1, testVideoPath1, + "My trip title", null, null); assertTrue(MediaUtils.isVideoMimeType(videoMedia1.getMimeType())); - MediaModel videoMedia2 = generateMediaFromPath(testSiteId, testVideoId2, testVideoPath2); - videoMedia2.setTitle("Test video title"); + MediaModel videoMedia2 = generateMediaFromPath(testSiteId, testVideoId2, testVideoPath2, + "Test video title", null, null); assertTrue(MediaUtils.isVideoMimeType(videoMedia2.getMimeType())); - MediaModel documentMedia = generateMediaFromPath(testSiteId, testDocumentId, testDocumentPath); - documentMedia.setTitle("My first test"); + MediaModel documentMedia = generateMediaFromPath(testSiteId, testDocumentId, testDocumentPath, + "My first test", null, null); assertTrue(MediaUtils.isApplicationMimeType(documentMedia.getMimeType())); // insert media of different types - insertMediaIntoDatabase(videoMedia1); - insertMediaIntoDatabase(videoMedia2); - insertMediaIntoDatabase(documentMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia1); + mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia2); + mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia); // verify the correct media is returned final List storeVideos = mMediaStore @@ -166,29 +182,27 @@ public void testSearchSiteAudio() { final long testDocumentId = 43; // generate media of different types - MediaModel imageMedia = generateMediaFromPath(testSiteId, testImageId, testImagePath); - imageMedia.setTitle("Title test"); + MediaModel imageMedia = generateMediaFromPath(testSiteId, testImageId, testImagePath, + "Title test", null, null); assertTrue(MediaUtils.isImageMimeType(imageMedia.getMimeType())); - MediaModel audioMedia1 = generateMediaFromPath(testSiteId, testAudioId1, testAudioPath1); - audioMedia1.setTitle("The big one"); - audioMedia1.setDescription("Test for the World"); + MediaModel audioMedia1 = generateMediaFromPath(testSiteId, testAudioId1, testAudioPath1, + "The big one", "Test for the World", null); assertTrue(MediaUtils.isAudioMimeType(audioMedia1.getMimeType())); - MediaModel audioMedia2 = generateMediaFromPath(testSiteId, testAudioId2, testAudioPath2); - audioMedia2.setTitle("The test!"); - audioMedia2.setDescription("Without description"); + MediaModel audioMedia2 = generateMediaFromPath(testSiteId, testAudioId2, testAudioPath2, + "The test!", "Without description", null); assertTrue(MediaUtils.isAudioMimeType(audioMedia2.getMimeType())); - MediaModel documentMedia = generateMediaFromPath(testSiteId, testDocumentId, testDocumentPath); - documentMedia.setTitle("Document with every test of the app"); + MediaModel documentMedia = generateMediaFromPath(testSiteId, testDocumentId, testDocumentPath, + "Document with every test of the app", null, null); assertTrue(MediaUtils.isApplicationMimeType(documentMedia.getMimeType())); // insert media of different types - insertMediaIntoDatabase(imageMedia); - insertMediaIntoDatabase(audioMedia1); - insertMediaIntoDatabase(audioMedia2); - insertMediaIntoDatabase(documentMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, imageMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia1); + mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia2); + mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia); // verify the correct media is returned (just audio) final List storeAudio = mMediaStore @@ -221,38 +235,32 @@ public void testSearchSiteDocuments() { final long testDocumentId4 = 543; // generate media of different types - MediaModel audioMedia = generateMediaFromPath(testSiteId, testAudioId, testAudioPath); - audioMedia.setTitle("My first test"); - audioMedia.setDescription("This is a description test"); - audioMedia.setCaption("Caption test"); + MediaModel audioMedia = generateMediaFromPath(testSiteId, testAudioId, testAudioPath, + "My first test", "This is a description test", "Caption test"); assertTrue(MediaUtils.isAudioMimeType(audioMedia.getMimeType())); - MediaModel documentMedia1 = generateMediaFromPath(testSiteId, testDocumentId1, testDocumentPath1); - documentMedia1.setTitle("The Document"); - documentMedia1.setDescription("short description"); + MediaModel documentMedia1 = generateMediaFromPath(testSiteId, testDocumentId1, testDocumentPath1, + "The Document", "short description", null); assertTrue(MediaUtils.isApplicationMimeType(documentMedia1.getMimeType())); - MediaModel documentMedia2 = generateMediaFromPath(testSiteId, testDocumentId2, testDocumentPath2); - documentMedia2.setTitle("Document to Test"); - documentMedia2.setDescription("medium description"); + MediaModel documentMedia2 = generateMediaFromPath(testSiteId, testDocumentId2, testDocumentPath2, + "Document to Test", "medium description", null); assertTrue(MediaUtils.isApplicationMimeType(documentMedia2.getMimeType())); - MediaModel documentMedia3 = generateMediaFromPath(testSiteId, testDocumentId3, testDocumentPath3); - documentMedia3.setTitle("Document"); - documentMedia3.setDescription("Large description with a test"); + MediaModel documentMedia3 = generateMediaFromPath(testSiteId, testDocumentId3, testDocumentPath3, + "Document", "Large description with a test", null); assertTrue(MediaUtils.isApplicationMimeType(documentMedia3.getMimeType())); - MediaModel documentMedia4 = generateMediaFromPath(testSiteId, testDocumentId4, testDocumentPath4); - documentMedia4.setTitle("Document Title"); - documentMedia4.setDescription("description"); + MediaModel documentMedia4 = generateMediaFromPath(testSiteId, testDocumentId4, testDocumentPath4, + "Document Title", "description", null); assertTrue(MediaUtils.isApplicationMimeType(documentMedia4.getMimeType())); // insert media of different types - insertMediaIntoDatabase(audioMedia); - insertMediaIntoDatabase(documentMedia1); - insertMediaIntoDatabase(documentMedia2); - insertMediaIntoDatabase(documentMedia3); - insertMediaIntoDatabase(documentMedia4); + mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia); + mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia1); + mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia2); + mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia3); + mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia4); // verify the correct media is returned (just documents) final List storeDocuments = mMediaStore diff --git a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java index e76c5f127f28..9839b1f0d466 100644 --- a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java +++ b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java @@ -1,71 +1,31 @@ package org.wordpress.android.fluxc.media; import org.wordpress.android.fluxc.model.MediaModel; -import org.wordpress.android.fluxc.persistence.MediaSqlUtils; import org.wordpress.android.fluxc.utils.MediaUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import static org.junit.Assert.assertEquals; - public class MediaTestUtils { - public static int insertMediaIntoDatabase(MediaModel media) { - return MediaSqlUtils.insertOrUpdateMedia(media); - } - - public static List insertRandomMediaIntoDatabase(int localSiteId, int count) { - List insertedMedia = generateRandomizedMediaList(count, localSiteId); - for (MediaModel media : insertedMedia) { - assertEquals(1, MediaSqlUtils.insertOrUpdateMedia(media)); - } - return insertedMedia; + public static MediaModel generateMediaFromPath(int localSiteId, long mediaId, String filePath) { + return generateMediaFromPath(localSiteId, mediaId, filePath, null, null, null); } - public static MediaModel generateMedia(String title, String desc, String caption, String alt) { - MediaModel media = new MediaModel( - 0, - 0 - ); - media.setTitle(title); - media.setDescription(desc); - media.setCaption(caption); - media.setAlt(alt); - return media; - } + //Temporary hack + private static int sNextId = 1; - public static MediaModel generateMediaFromPath(int localSiteId, long mediaId, String filePath) { - MediaModel media = new MediaModel( - localSiteId, - mediaId - ); + public static MediaModel generateMediaFromPath(int localSiteId, long mediaId, String filePath, + String title, String description, String caption) { + MediaModel media = new MediaModel(localSiteId, mediaId); + media.setId(sNextId++); // Assign unique ID for cache media.setFilePath(filePath); media.setFileName(MediaUtils.getFileName(filePath)); String extension = MediaUtils.getExtension(filePath); media.setMimeType(MediaUtils.getMimeTypeForExtension(extension)); - media.setTitle(media.getFileName()); - return media; - } - - public static MediaModel generateRandomizedMedia(int localSiteId) { - MediaModel media = generateMedia(randomStr(5), randomStr(5), randomStr(5), randomStr(5)); - media.setLocalSiteId(localSiteId); - return media; - } - - public static List generateRandomizedMediaList(int size, int localSiteId) { - List mediaList = new ArrayList<>(); - for (int i = 0; i < size; ++i) { - MediaModel newMedia = generateRandomizedMedia(localSiteId); - newMedia.setMediaId(i); - mediaList.add(newMedia); + media.setTitle(title != null ? title : media.getFileName()); + if (description != null) { + media.setDescription(description); } - return mediaList; - } - - public static String randomStr(int length) { - String randomString = UUID.randomUUID().toString(); - return length > randomString.length() ? randomString : randomString.substring(0, length); + if (caption != null) { + media.setCaption(caption); + } + return media; } } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt new file mode 100644 index 000000000000..6aa181571f41 --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt @@ -0,0 +1,78 @@ +package org.wordpress.android.fluxc.store + +import org.wordpress.android.fluxc.model.MediaModel +import java.util.Locale +import javax.inject.Inject + +class MediaCacheOperations @Inject constructor( + private val cache: MediaLibraryCache +) { + fun getSiteImages(siteId: Int): List { + return filterByMimeType(siteId, "image") + } + + fun getSiteVideos(siteId: Int): List { + return filterByMimeType(siteId, "video") + } + + fun getSiteAudio(siteId: Int): List { + return filterByMimeType(siteId, "audio") + } + + fun getSiteDocuments(siteId: Int): List { + return filterByMimeType(siteId, "application") + } + + fun searchSiteImages(siteId: Int, searchTerm: String): List { + return searchByMimeTypeAndTerm(siteId, "image", searchTerm) + } + + fun searchSiteVideos(siteId: Int, searchTerm: String): List { + return searchByMimeTypeAndTerm(siteId, "video", searchTerm) + } + + fun searchSiteAudio(siteId: Int, searchTerm: String): List { + return searchByMimeTypeAndTerm(siteId, "audio", searchTerm) + } + + fun searchSiteDocuments(siteId: Int, searchTerm: String): List { + return searchByMimeTypeAndTerm(siteId, "application", searchTerm) + } + + fun getCacheSize(siteId: Int): Int { + return cache.getMediaList(siteId)?.size ?: 0 + } + + fun getUploadedMediaCount(siteId: Int, mimeTypePrefix: String?): Int { + if (mimeTypePrefix == null) { + return getCacheSize(siteId) + } + return filterByMimeType(siteId, mimeTypePrefix).size + } + + private fun filterByMimeType(siteId: Int, mimeTypePrefix: String): List { + val allMedia = cache.getMediaList(siteId) ?: return emptyList() + return allMedia.filter { media -> + media.mimeType?.startsWith(mimeTypePrefix) == true + } + } + + private fun searchByMimeTypeAndTerm( + siteId: Int, + mimeTypePrefix: String, + searchTerm: String + ): List { + val allMedia = cache.getMediaList(siteId) ?: return emptyList() + val lowerSearchTerm = searchTerm.lowercase(Locale.ROOT) + return allMedia.filter { media -> + media.mimeType?.startsWith(mimeTypePrefix) == true && + matchesSearchTerm(media, lowerSearchTerm) + } + } + + private fun matchesSearchTerm(media: MediaModel, lowerSearchTerm: String): Boolean { + return (media.title?.contains(lowerSearchTerm, ignoreCase = true) == true) || + media.caption.contains(lowerSearchTerm, ignoreCase = true) || + media.description.contains(lowerSearchTerm, ignoreCase = true) + } +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java index caed1b38b4ce..02e37f07a782 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java @@ -420,6 +420,9 @@ public static MediaErrorType fromString(@Nullable String string) { private final WPComV2MediaRestClient mWPComV2MediaRestClient; private final ApplicationPasswordsMediaRestClient mApplicationPasswordsMediaRestClient; + @NonNull private final MediaLibraryCache mMediaLibraryCache; + @NonNull private final MediaCacheOperations mMediaCacheOperations; + @NonNull private final MediaIdGenerator mMediaIdGenerator; private final ApplicationPasswordsConfiguration mApplicationPasswordsConfiguration; @@ -430,12 +433,18 @@ public static MediaErrorType fromString(@Nullable String string) { WPComV2MediaRestClient wpv2MediaRestClient, ApplicationPasswordsMediaRestClient applicationPasswordsMediaRestClient, ApplicationPasswordsConfiguration applicationPasswordsConfiguration, - @NonNull FluxCCrashLogger crashLogger) { + @NonNull FluxCCrashLogger crashLogger, + @NonNull MediaLibraryCache mediaLibraryCache, + @NonNull MediaCacheOperations mediaCacheOperations, + @NonNull MediaIdGenerator mediaIdGenerator) { super(dispatcher); mWPComV2MediaRestClient = wpv2MediaRestClient; mApplicationPasswordsMediaRestClient = applicationPasswordsMediaRestClient; mApplicationPasswordsConfiguration = applicationPasswordsConfiguration; mCrashLogger = crashLogger; + mMediaLibraryCache = mediaLibraryCache; + mMediaCacheOperations = mediaCacheOperations; + mMediaIdGenerator = mediaIdGenerator; } @Subscribe(threadMode = ThreadMode.ASYNC) @@ -488,67 +497,56 @@ public void onRegister() { @Nullable public MediaModel instantiateMediaModel(@NonNull MediaModel media) { - MediaModel insertedMedia = MediaSqlUtils.insertMediaForResult(media); - - if (insertedMedia.getId() == -1) { - return null; - } - - return insertedMedia; - } - - @Nullable - public MediaModel getSiteMediaWithId(@NonNull SiteModel siteModel, long mediaId) { - List media = MediaSqlUtils.getSiteMediaWithId(siteModel, mediaId); - return media.size() > 0 ? media.get(0) : null; + media.setId(mMediaIdGenerator.generate(media.getFilePath()).getValue()); + return media; } @NonNull public List getSiteImages(@NonNull SiteModel siteModel) { - return MediaSqlUtils.getSiteImages(siteModel); + return mMediaCacheOperations.getSiteImages(siteModel.getId()); } @NonNull public List getSiteVideos(@NonNull SiteModel siteModel) { - return MediaSqlUtils.getSiteVideos(siteModel); + return mMediaCacheOperations.getSiteVideos(siteModel.getId()); } @NonNull public List getSiteAudio(@NonNull SiteModel siteModel) { - return MediaSqlUtils.getSiteAudio(siteModel); + return mMediaCacheOperations.getSiteAudio(siteModel.getId()); } @NonNull public List getSiteDocuments(@NonNull SiteModel siteModel) { - return MediaSqlUtils.getSiteDocuments(siteModel); + return mMediaCacheOperations.getSiteDocuments(siteModel.getId()); } @NonNull public List searchSiteImages( @NonNull SiteModel siteModel, @NonNull String searchTerm) { - return MediaSqlUtils.searchSiteImages(siteModel, searchTerm); + return mMediaCacheOperations.searchSiteImages(siteModel.getId(), searchTerm); } @NonNull public List searchSiteVideos( @NonNull SiteModel siteModel, @NonNull String searchTerm) { - return MediaSqlUtils.searchSiteVideos(siteModel, searchTerm); + return mMediaCacheOperations.searchSiteVideos(siteModel.getId(), searchTerm); } @NonNull public List searchSiteAudio( @NonNull SiteModel siteModel, @NonNull String searchTerm) { - return MediaSqlUtils.searchSiteAudio(siteModel, searchTerm); + return mMediaCacheOperations.searchSiteAudio(siteModel.getId(), searchTerm); } @NonNull public List searchSiteDocuments( @NonNull SiteModel siteModel, @NonNull String searchTerm) { - return MediaSqlUtils.searchSiteDocuments(siteModel, searchTerm); + return mMediaCacheOperations.searchSiteDocuments(siteModel.getId(), searchTerm); } // @@ -560,10 +558,9 @@ void updateMedia(@Nullable MediaModel media, boolean emit) { if (media == null) { event.error = new MediaError(MediaErrorType.NULL_MEDIA_ARG); - } else if (MediaSqlUtils.insertOrUpdateMedia(media) > 0) { - event.mediaList.add(media); } else { - event.error = new MediaError(MediaErrorType.DB_QUERY_FAILURE); + mMediaLibraryCache.addOrUpdate(media.getLocalSiteId(), media); + event.mediaList.add(media); } if (emit) { @@ -601,8 +598,6 @@ private void performUploadMedia(@NonNull UploadMediaPayload payload) { if (argError.getType() != Type.NO_ERROR) { String message = "Media doesn't have required data: " + argError.getType().getErrorLogDescription(); AppLog.e(AppLog.T.MEDIA, message); - payload.media.setUploadState(MediaUploadState.FAILED); - MediaSqlUtils.insertOrUpdateMedia(payload.media); notifyMediaUploadError( MediaErrorType.MALFORMED_MEDIA_ARG, argError.getType().getErrorLogDescription(), @@ -612,9 +607,6 @@ private void performUploadMedia(@NonNull UploadMediaPayload payload) { return; } - payload.media.setUploadState(MediaUploadState.UPLOADING); - MediaSqlUtils.insertOrUpdateMedia(payload.media); - if (payload.stripLocation) { MediaUtils.stripLocation(payload.media.getFilePath()); } @@ -632,14 +624,8 @@ private void performUploadMedia(@NonNull UploadMediaPayload payload) { private void performFetchMediaList(@NonNull FetchMediaListPayload payload) { int offset = 0; if (payload.loadMore) { - List list = new ArrayList<>(); - list.add(MediaUploadState.UPLOADED.toString()); - if (payload.mimeType != null) { - offset = MediaSqlUtils.getMediaWithStatesAndMimeType(payload.site, list, payload.mimeType.getValue()) - .size(); - } else { - offset = MediaSqlUtils.getMediaWithStates(payload.site, list).size(); - } + String mimeTypeValue = payload.mimeType != null ? payload.mimeType.getValue() : null; + offset = mMediaCacheOperations.getUploadedMediaCount(payload.site.getId(), mimeTypeValue); } if (payload.site.getOrigin() == SiteModel.ORIGIN_WPCOM_REST) { mWPComV2MediaRestClient.fetchMediaList(payload.site, payload.number, offset, payload.mimeType); @@ -654,10 +640,7 @@ private void performFetchMediaList(@NonNull FetchMediaListPayload payload) { private void performCancelUpload(@NonNull CancelMediaPayload payload) { MediaModel media = payload.media; if (payload.delete) { - MediaSqlUtils.deleteMedia(media); - } else { - media.setUploadState(MediaUploadState.FAILED); - MediaSqlUtils.insertOrUpdateMedia(media); + mMediaLibraryCache.remove(payload.site.getId(), media.getMediaId()); } if (payload.site.getOrigin() == SiteModel.ORIGIN_WPCOM_REST) { @@ -671,7 +654,7 @@ private void performCancelUpload(@NonNull CancelMediaPayload payload) { } private void handleMediaUploaded(@NonNull ProgressPayload payload) { - if (payload.isError() || payload.canceled || payload.completed) { + if (payload.completed && !payload.isError() && !payload.canceled) { updateMedia(payload.media, false); } OnMediaUploaded onMediaUploaded = new OnMediaUploaded( @@ -697,39 +680,19 @@ private void handleMediaCanceled(@NonNull ProgressPayload payload) { } private void updateFetchedMediaList(@NonNull FetchMediaListResponsePayload payload) { - // if we loaded another page, simply add the fetched media and be done - if (payload.loadedMore) { - for (MediaModel media : payload.mediaList) { - updateMedia(media, false); - } - return; - } + List currentCache = mMediaLibraryCache.getMediaList(payload.site.getId()); - // build separate lists of existing and new media - List existingMediaList = new ArrayList<>(); - List newMediaList = new ArrayList<>(); - for (MediaModel fetchedMedia : payload.mediaList) { - MediaModel media = getSiteMediaWithId(payload.site, fetchedMedia.getMediaId()); - if (media != null) { - // retain the local ID, then update this media item - fetchedMedia.setId(media.getId()); - existingMediaList.add(fetchedMedia); - updateMedia(fetchedMedia, false); - } else { - newMediaList.add(fetchedMedia); + if (payload.loadedMore) { + // Append to existing cache + if (currentCache == null) { + currentCache = new ArrayList<>(); } - } - - // remove media that is NOT in the existing list - String mimeTypeValue = ""; - if (payload.mimeType != null) { - mimeTypeValue = payload.mimeType.getValue(); - } - MediaSqlUtils.deleteUploadedSiteMediaNotInList(payload.site, existingMediaList, mimeTypeValue); - - // add new media - for (MediaModel media : newMediaList) { - updateMedia(media, false); + List updatedList = new ArrayList<>(currentCache); + updatedList.addAll(payload.mediaList); + mMediaLibraryCache.cacheMediaList(payload.site.getId(), updatedList); + } else { + // Replace entire cache with fresh data + mMediaLibraryCache.cacheMediaList(payload.site.getId(), new ArrayList<>(payload.mediaList)); } } @@ -749,7 +712,7 @@ private void handleMediaListFetched(@NonNull FetchMediaListResponsePayload paylo private void handleMediaFetched(@NonNull MediaPayload payload) { OnMediaChanged onMediaChanged = new OnMediaChanged(MediaAction.FETCH_MEDIA, payload.error); if (payload.media != null) { - MediaSqlUtils.insertOrUpdateMedia(payload.media); + mMediaLibraryCache.addOrUpdate(payload.site.getId(), payload.media); onMediaChanged.mediaList = new ArrayList<>(); onMediaChanged.mediaList.add(payload.media); } From efabd2ecdda3987416617d8b562ab05a8b9b8950 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 15 Jan 2026 16:38:10 +0100 Subject: [PATCH 03/27] Remove `mLocalPostId` from `MediaModel` It wasn't used anywhere --- .../org/wordpress/android/fluxc/model/MediaModel.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index 86af8ad26e5b..2e4c964cb67a 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -40,7 +40,6 @@ public static MediaUploadState fromString(@Nullable String stringState) { // Associated IDs @Column private int mLocalSiteId; - @Column private int mLocalPostId; // The local post the media was uploaded from, for lookup after media uploads @Column private long mMediaId; // The remote ID of the media @Column private long mPostId; // The remote post ID ('parent') of the media @@ -99,7 +98,6 @@ public String getFieldName() { public MediaModel() { this.mId = 0; this.mLocalSiteId = 0; - this.mLocalPostId = 0; this.mMediaId = 0; this.mPostId = 0; this.mUploadDate = null; @@ -242,7 +240,6 @@ public boolean equals(@Nullable Object other) { return getId() == otherMedia.getId() && getLocalSiteId() == otherMedia.getLocalSiteId() - && getLocalPostId() == otherMedia.getLocalPostId() && getMediaId() == otherMedia.getMediaId() && getPostId() == otherMedia.getPostId() && getMarkedLocallyAsFeatured() == otherMedia.getMarkedLocallyAsFeatured() @@ -277,14 +274,6 @@ public int getLocalSiteId() { return mLocalSiteId; } - public void setLocalPostId(int localPostId) { - mLocalPostId = localPostId; - } - - public int getLocalPostId() { - return mLocalPostId; - } - public void setMediaId(long mediaId) { mMediaId = mediaId; } From bdbcb08eb1e7a32fa1a3f36d057978643b4a7782 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 15 Jan 2026 16:39:26 +0100 Subject: [PATCH 04/27] Remove `MediaFields` from `MediaModel` As it's unused --- .../android/fluxc/model/MediaModel.java | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index 2e4c964cb67a..f0d3ac45bed8 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -65,34 +65,6 @@ public static MediaUploadState fromString(@Nullable String stringState) { @Nullable @Column private String mUploadState; @Column private boolean mMarkedLocallyAsFeatured; - /** - * Enum representing various media fields with their default field names. - * The default values can be changed by modifying the string parameter - * passed to the enum constructor. - */ - public enum MediaFields { - PARENT_ID("parent_id"), - TITLE("title"), - DESCRIPTION("description"), - CAPTION("caption"), - ALT("alt"); - - @NonNull private final String mFieldName; - - // Constructor - MediaFields(@NonNull String fieldName) { - this.mFieldName = fieldName; - } - - // Getter - @NonNull - public String getFieldName() { - return this.mFieldName; - } - } - - @NonNull private MediaFields[] mFieldsToUpdate = MediaFields.values(); - @Deprecated @SuppressWarnings("DeprecatedIsStillUsed") public MediaModel() { @@ -393,16 +365,6 @@ public String getUploadState() { return mUploadState; } - @NonNull - public MediaFields[] getFieldsToUpdate() { - return mFieldsToUpdate; - } - - @SuppressWarnings("unused") - public void setFieldsToUpdate(@NonNull MediaFields[] fieldsToUpdate) { - this.mFieldsToUpdate = fieldsToUpdate; - } - public boolean getMarkedLocallyAsFeatured() { return mMarkedLocallyAsFeatured; } From cbd1684189180e9e7a592c9e6de697465ba92f70 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 15 Jan 2026 17:43:03 +0100 Subject: [PATCH 05/27] Remove unused constructors from `MediaModel` --- .../android/fluxc/model/MediaModel.java | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index f0d3ac45bed8..23230eeaa753 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -124,55 +124,6 @@ public MediaModel( this.mUploadState = uploadState != null ? uploadState.toString() : null; } - /** - * Use when converting editor image metadata into a media. - */ - public MediaModel( - @NonNull String url, - @Nullable String fileName, - @Nullable String title, - @NonNull String caption, - @NonNull String alt) { - this.mUrl = url; - this.mFileName = fileName; - this.mTitle = title; - this.mCaption = caption; - this.mDescription = ""; - this.mAlt = alt; - } - - /** - * Use when converting a media file into a media. - */ - public MediaModel( - int id, - int localSiteId, - long mediaId, - @NonNull String url, - @Nullable String thumbnailUrl, - @Nullable String fileName, - @Nullable String filePath, - @Nullable String mimeType, - @Nullable String title, - @NonNull String caption, - @NonNull String description, - @NonNull String alt, - @NonNull MediaUploadState uploadState) { - this.mId = id; - this.mLocalSiteId = localSiteId; - this.mMediaId = mediaId; - this.mUrl = url; - this.mThumbnailUrl = thumbnailUrl; - this.mFileName = fileName; - this.mFilePath = filePath; - this.mMimeType = mimeType; - this.mTitle = title; - this.mCaption = caption; - this.mDescription = description; - this.mAlt = alt; - this.mUploadState = uploadState.toString(); - } - public MediaModel( int localSiteId, long mediaId, From 64deb8dd2a66a394ad06669251ccae5a5c1cdf85 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 15 Jan 2026 17:44:58 +0100 Subject: [PATCH 06/27] Remove `MediaSqlUtils` We don't use database anymore for storing `MediaModel` --- .../fluxc/media/MediaSqlUtilsTest.java | 141 -------- .../fluxc/persistence/MediaSqlUtils.java | 310 ------------------ .../android/fluxc/store/MediaStore.java | 1 - 3 files changed, 452 deletions(-) delete mode 100644 libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaSqlUtilsTest.java delete mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/MediaSqlUtils.java diff --git a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaSqlUtilsTest.java b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaSqlUtilsTest.java deleted file mode 100644 index 1ef26971525c..000000000000 --- a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaSqlUtilsTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.wordpress.android.fluxc.media; - -import static org.assertj.core.api.Assertions.assertThat; - -import android.content.Context; - -import com.wellsql.generated.MediaModelTable; -import com.yarolegovich.wellsql.WellSql; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.wordpress.android.fluxc.SingleStoreWellSqlConfigForTests; -import org.wordpress.android.fluxc.model.MediaModel; -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState; -import org.wordpress.android.fluxc.model.SiteModel; -import org.wordpress.android.fluxc.persistence.MediaSqlUtils; -import org.wordpress.android.fluxc.persistence.WellSqlConfig; -import org.wordpress.android.fluxc.utils.MimeType.Type; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -@RunWith(RobolectricTestRunner.class) -public class MediaSqlUtilsTest { - private static final int TEST_LOCAL_SITE_ID = 42; - private static final int SMALL_TEST_POOL = 10; - - private final Random mRandom = new Random(System.currentTimeMillis()); - - @Before - public void setUp() { - Context appContext = RuntimeEnvironment.getApplication().getApplicationContext(); - - WellSqlConfig config = new SingleStoreWellSqlConfigForTests(appContext, MediaModel.class); - WellSql.init(config); - config.reset(); - } - - // Inserts a media item with various known fields then retrieves and validates those fields - @Test - public void testInsertMedia() { - long testId = Math.abs(mRandom.nextLong()); - String testTitle = getTestString(); - String testDescription = getTestString(); - String testCaption = getTestString(); - MediaModel testMedia = getTestMedia(testId, testTitle, testDescription, testCaption); - assertThat(1).isEqualTo(MediaSqlUtils.insertOrUpdateMedia(testMedia)); - List media = MediaSqlUtils.getSiteMediaWithId(getTestSiteWithLocalId(TEST_LOCAL_SITE_ID), testId); - assertThat(media).hasSize(1); - assertThat(media.get(0)).isNotNull(); - assertThat(media.get(0).getMediaId()).isEqualTo(testId); - assertThat(media.get(0).getTitle()).isEqualTo(testTitle); - assertThat(media.get(0).getDescription()).isEqualTo(testDescription); - assertThat(media.get(0).getCaption()).isEqualTo(testCaption); - } - - // Inserts media of multiple MIME types then retrieves only images and verifies - @Test - public void testGetSiteImages() { - List imageIds = new ArrayList<>(SMALL_TEST_POOL); - List videoIds = new ArrayList<>(SMALL_TEST_POOL); - for (int i = 0; i < imageIds.size(); ++i) { - imageIds.add(mRandom.nextLong()); - videoIds.add(mRandom.nextLong()); - MediaModel image = getTestMedia(imageIds.get(i)); - image.setMimeType("image/jpg"); - MediaModel video = getTestMedia(videoIds.get(i)); - video.setMimeType("video/mp4"); - assertThat(MediaSqlUtils.insertOrUpdateMedia(image)).isEqualTo(0); - assertThat(MediaSqlUtils.insertOrUpdateMedia(video)).isEqualTo(0); - } - List images = MediaSqlUtils.getSiteImages(getTestSiteWithLocalId(TEST_LOCAL_SITE_ID)); - assertThat(imageIds.size()).isEqualTo(images.size()); - for (int i = 0; i < imageIds.size(); ++i) { - assertThat(images.get(0).getMimeType().contains(Type.IMAGE.getValue())).isTrue(); - assertThat(imageIds).contains(images.get(i).getMediaId()); - } - } - - // Inserts many images then retrieves all images with a supplied exclusion filter - @Test - public void testGetSiteImagesExclusionFilter() { - long[] imageIds = insertImageTestItems(); - List exclusion = new ArrayList<>(); - for (int i = 0; i < SMALL_TEST_POOL; i += 2) { - exclusion.add(imageIds[i]); - } - List includedImages = MediaSqlUtils - .getSiteImagesExcluding(getTestSiteWithLocalId(TEST_LOCAL_SITE_ID), exclusion); - assertThat(includedImages).hasSize(SMALL_TEST_POOL - exclusion.size()); - for (int i = 0; i < includedImages.size(); ++i) { - assertThat(exclusion).doesNotContain(includedImages.get(i).getMediaId()); - } - } - - // Utilities - - private long[] insertImageTestItems() { - long[] testItemIds = new long[MediaSqlUtilsTest.SMALL_TEST_POOL]; - for (int i = 0; i < MediaSqlUtilsTest.SMALL_TEST_POOL; ++i) { - testItemIds[i] = Math.abs(mRandom.nextInt()); - MediaModel image = getTestMedia(testItemIds[i]); - image.setMimeType("image/jpg"); - image.setUploadState(MediaUploadState.UPLOADED); - assertThat(1).isEqualTo(MediaSqlUtils.insertOrUpdateMedia(image)); - } - return testItemIds; - } - - private MediaModel getTestMedia(long mediaId) { - return new MediaModel( - TEST_LOCAL_SITE_ID, - mediaId - ); - } - - private MediaModel getTestMedia(long mediaId, String title, String description, String caption) { - MediaModel media = new MediaModel( - TEST_LOCAL_SITE_ID, - mediaId - ); - media.setTitle(title); - media.setDescription(description); - media.setCaption(caption); - return media; - } - - private String getTestString() { - return "BaseTestString-" + mRandom.nextInt(); - } - - private SiteModel getTestSiteWithLocalId(int localSiteId) { - SiteModel siteModel = new SiteModel(); - siteModel.setId(localSiteId); - return siteModel; - } -} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/MediaSqlUtils.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/MediaSqlUtils.java deleted file mode 100644 index 8ba78270a371..000000000000 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/MediaSqlUtils.java +++ /dev/null @@ -1,310 +0,0 @@ -package org.wordpress.android.fluxc.persistence; - -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.wellsql.generated.MediaModelTable; -import com.yarolegovich.wellsql.ConditionClauseBuilder; -import com.yarolegovich.wellsql.DeleteQuery; -import com.yarolegovich.wellsql.SelectQuery; -import com.yarolegovich.wellsql.WellSql; - -import org.wordpress.android.fluxc.model.MediaModel; -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState; -import org.wordpress.android.fluxc.model.SiteModel; -import org.wordpress.android.fluxc.utils.MimeType.Type; - -import java.util.ArrayList; -import java.util.List; - -public class MediaSqlUtils { - @NonNull - public static List getMediaWithStates( - @NonNull SiteModel site, - @NonNull List uploadStates) { - return getMediaWithStatesQuery(site, uploadStates).getAsModel(); - } - - @NonNull - public static List getMediaWithStatesAndMimeType( - @NonNull SiteModel site, - @NonNull List uploadStates, - @NonNull String mimeType) { - return WellSql.select(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, site.getId()) - .contains(MediaModelTable.MIME_TYPE, mimeType) - .isIn(MediaModelTable.UPLOAD_STATE, uploadStates) - .endGroup().endWhere() - .orderBy(MediaModelTable.UPLOAD_DATE, SelectQuery.ORDER_DESCENDING) - .getAsModel(); - } - - @NonNull - private static SelectQuery getMediaWithStatesQuery( - @NonNull SiteModel site, - @NonNull List uploadStates) { - return WellSql.select(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, site.getId()) - .isIn(MediaModelTable.UPLOAD_STATE, uploadStates) - .endGroup().endWhere() - .orderBy(MediaModelTable.UPLOAD_DATE, SelectQuery.ORDER_DESCENDING); - } - - @NonNull - public static List getSiteMediaWithId(@NonNull SiteModel siteModel, long mediaId) { - return WellSql.select(MediaModel.class).where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, siteModel.getId()) - .equals(MediaModelTable.MEDIA_ID, mediaId) - .endGroup().endWhere() - .orderBy(MediaModelTable.UPLOAD_DATE, SelectQuery.ORDER_DESCENDING) - .getAsModel(); - } - - @NonNull - public static List searchSiteImages( - @NonNull SiteModel siteModel, - @NonNull String searchTerm) { - return searchSiteMediaByMimeTypeQuery(siteModel, searchTerm, Type.IMAGE.getValue()).getAsModel(); - } - - @NonNull - public static List searchSiteAudio( - @NonNull SiteModel siteModel, - @NonNull String searchTerm) { - return searchSiteMediaByMimeTypeQuery(siteModel, searchTerm, Type.AUDIO.getValue()).getAsModel(); - } - - @NonNull - public static List searchSiteVideos( - @NonNull SiteModel siteModel, - @NonNull String searchTerm) { - return searchSiteMediaByMimeTypeQuery(siteModel, searchTerm, Type.VIDEO.getValue()).getAsModel(); - } - - @NonNull - public static List searchSiteDocuments( - @NonNull SiteModel siteModel, - @NonNull String searchTerm) { - return searchSiteMediaByMimeTypeQuery(siteModel, searchTerm, Type.APPLICATION.getValue()).getAsModel(); - } - - @NonNull - private static SelectQuery searchSiteMediaByMimeTypeQuery( - @NonNull SiteModel siteModel, - @NonNull String searchTerm, - @NonNull String mimeTypePrefix) { - return WellSql.select(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, siteModel.getId()) - .contains(MediaModelTable.MIME_TYPE, mimeTypePrefix) - .beginGroup() - .contains(MediaModelTable.TITLE, searchTerm) - .or().contains(MediaModelTable.CAPTION, searchTerm) - .or().contains(MediaModelTable.DESCRIPTION, searchTerm) - .endGroup() - .endGroup().endWhere() - .orderBy(MediaModelTable.UPLOAD_DATE, SelectQuery.ORDER_DESCENDING); - } - - @NonNull - public static List getSiteImages(@NonNull SiteModel siteModel) { - return getSiteImagesQuery(siteModel).getAsModel(); - } - - @NonNull - private static SelectQuery getSiteImagesQuery(@NonNull SiteModel siteModel) { - return getSiteMediaByMimeTypeQuery(siteModel, Type.IMAGE.getValue()); - } - - @NonNull - public static List getSiteImagesExcluding( - @NonNull SiteModel siteModel, - @NonNull List filter) { - return getSiteImagesExcludingQuery(siteModel, filter).getAsModel(); - } - - @NonNull - public static List getSiteVideos(@NonNull SiteModel siteModel) { - return getSiteVideosQuery(siteModel).getAsModel(); - } - - @NonNull - public static List getSiteDocuments(@NonNull SiteModel siteModel) { - return getSiteDocumentsQuery(siteModel).getAsModel(); - } - - @NonNull - public static List getSiteAudio(@NonNull SiteModel siteModel) { - return getSiteAudioQuery(siteModel).getAsModel(); - } - - @NonNull - public static SelectQuery getSiteImagesExcludingQuery( - @NonNull SiteModel siteModel, - @NonNull List filter) { - return WellSql.select(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, siteModel.getId()) - .contains(MediaModelTable.MIME_TYPE, Type.IMAGE.getValue()) - .isNotIn(MediaModelTable.MEDIA_ID, filter) - .endGroup().endWhere() - .orderBy(MediaModelTable.UPLOAD_DATE, SelectQuery.ORDER_DESCENDING); - } - - @NonNull - private static SelectQuery getSiteVideosQuery(@NonNull SiteModel siteModel) { - return getSiteMediaByMimeTypeQuery(siteModel, Type.VIDEO.getValue()); - } - - @NonNull - private static SelectQuery getSiteAudioQuery(@NonNull SiteModel siteModel) { - return getSiteMediaByMimeTypeQuery(siteModel, Type.AUDIO.getValue()); - } - - @NonNull - private static SelectQuery getSiteDocumentsQuery(@NonNull SiteModel siteModel) { - return getSiteMediaByMimeTypeQuery(siteModel, Type.APPLICATION.getValue()); - } - - @NonNull - private static SelectQuery getSiteMediaByMimeTypeQuery( - @NonNull SiteModel siteModel, - @NonNull String mimeTypePrefix) { - return WellSql.select(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, siteModel.getId()) - .contains(MediaModelTable.MIME_TYPE, mimeTypePrefix) - .endGroup().endWhere() - .orderBy(MediaModelTable.UPLOAD_DATE, SelectQuery.ORDER_DESCENDING); - } - - public static int insertOrUpdateMedia(@Nullable MediaModel media) { - if (media == null) return 0; - - List existingMedia; - if (media.getMediaId() == 0) { - // If the remote media ID is 0, this is a local media file and we should only match by local ID - // Otherwise, we'd match all local media files for that site - existingMedia = WellSql.select(MediaModel.class) - .where() - .equals(MediaModelTable.ID, media.getId()) - .endWhere().getAsModel(); - } else { - // For remote media, we can uniquely identify the media by either its local ID - // or its remote media ID + its (local) site ID - existingMedia = WellSql.select(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.ID, media.getId()) - .or() - .beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, media.getLocalSiteId()) - .equals(MediaModelTable.MEDIA_ID, media.getMediaId()) - .endGroup() - .endGroup().endWhere().getAsModel(); - } - - if (existingMedia.isEmpty()) { - // insert, media item does not exist - WellSql.insert(media).asSingleTransaction(true).execute(); - return 1; - } else { - if (existingMedia.size() > 1) { - // We've ended up with a duplicate entry, probably due to a push/fetch race condition - // One matches based on local ID (this is the one we're trying to update with a remote media ID) - // The other matches based on local site ID + remote media ID, and we got it from a fetch - // Just remove the entry without a remote media ID (the one matching the current media's local ID) - return WellSql.delete(MediaModel.class).whereId(media.getId()); - } - // update, media item already exists - int oldId = existingMedia.get(0).getId(); - return WellSql.update(MediaModel.class).whereId(oldId) - .put(media, new UpdateAllExceptId<>(MediaModel.class)).execute(); - } - } - - @NonNull - public static MediaModel insertMediaForResult(@NonNull MediaModel media) { - WellSql.insert(media).asSingleTransaction(true).execute(); - return media; - } - - public static int deleteMedia(@Nullable MediaModel media) { - if (media == null) return 0; - if (media.getMediaId() == 0) { - // If the remote media ID is 0, this is a local media file and we should only match by local ID - // Otherwise, we'd match all local media files for that site - return WellSql.delete(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.ID, media.getId()) - .endGroup().endWhere() - .execute(); - } else { - // For remote media, we can uniquely identify the media by either its local ID - // or its remote media ID + its (local) site ID - return WellSql.delete(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.ID, media.getId()) - .or() - .beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, media.getLocalSiteId()) - .equals(MediaModelTable.MEDIA_ID, media.getMediaId()) - .endGroup() - .endGroup().endWhere() - .execute(); - } - } - - public static void deleteAllUploadedSiteMedia(@NonNull SiteModel siteModel) { - WellSql.delete(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, siteModel.getId()) - .equals(MediaModelTable.UPLOAD_STATE, MediaUploadState.UPLOADED.toString()) - .endGroup().endWhere().execute(); - } - - public static void deleteAllUploadedSiteMediaWithMimeType( - @NonNull SiteModel siteModel, - @NonNull String mimeType) { - WellSql.delete(MediaModel.class) - .where().beginGroup() - .equals(MediaModelTable.LOCAL_SITE_ID, siteModel.getId()) - .equals(MediaModelTable.UPLOAD_STATE, MediaUploadState.UPLOADED.toString()) - .contains(MediaModelTable.MIME_TYPE, mimeType) - .endGroup().endWhere().execute(); - } - - public static void deleteUploadedSiteMediaNotInList( - @NonNull SiteModel site, - @NonNull List mediaList, - @NonNull String mimeType) { - if (mediaList.isEmpty()) { - if (!TextUtils.isEmpty(mimeType)) { - MediaSqlUtils.deleteAllUploadedSiteMediaWithMimeType(site, mimeType); - } else { - MediaSqlUtils.deleteAllUploadedSiteMedia(site); - } - return; - } - - List idList = new ArrayList<>(); - for (MediaModel media : mediaList) { - idList.add(media.getId()); - } - - ConditionClauseBuilder> builder = WellSql.delete(MediaModel.class) - .where().beginGroup() - .isNotIn(MediaModelTable.ID, idList) - .equals(MediaModelTable.LOCAL_SITE_ID, site.getId()) - .equals(MediaModelTable.UPLOAD_STATE, MediaUploadState.UPLOADED.toString()); - - if (!TextUtils.isEmpty(mimeType)) { - builder.contains(MediaModelTable.MIME_TYPE, mimeType); - } - - builder.endGroup().endWhere().execute(); - } -} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java index 02e37f07a782..48ccad39bab6 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java @@ -20,7 +20,6 @@ import org.wordpress.android.fluxc.network.rest.wpapi.applicationpasswords.ApplicationPasswordsConfiguration; import org.wordpress.android.fluxc.network.rest.wpapi.media.ApplicationPasswordsMediaRestClient; import org.wordpress.android.fluxc.network.rest.wpcom.media.wpv2.WPComV2MediaRestClient; -import org.wordpress.android.fluxc.persistence.MediaSqlUtils; import org.wordpress.android.fluxc.store.media.MediaErrorSubType; import org.wordpress.android.fluxc.store.media.MediaErrorSubType.MalformedMediaArgSubType; import org.wordpress.android.fluxc.store.media.MediaErrorSubType.MalformedMediaArgSubType.Type; From 9f35c4818be68cdeea99f60dd2bac061d1702042 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 15 Jan 2026 18:37:13 +0100 Subject: [PATCH 07/27] Remove unused `MediaModel` constructor The constructor was used only in tests. This commit also refactors and moves `MediaTestUtils` to testFixtures --- .../media/ProductImagesUploadWorkerTest.kt | 16 ++-- .../ui/media/MediaFileUploadHandlerTest.kt | 16 ++-- .../details/ProductDetailViewModelTest.kt | 4 +- .../android/fluxc/media/MediaTestUtils.java | 31 -------- .../android/fluxc/model/MediaModel.java | 14 ---- .../android/fluxc/media/MediaTestUtils.kt | 77 +++++++++++++++++++ 6 files changed, 95 insertions(+), 63 deletions(-) delete mode 100644 libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java create mode 100644 libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt index e4af37273e3f..738913a4feda 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt @@ -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 @@ -45,8 +45,8 @@ 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 { + private val FETCHED_MEDIA = MediaTestUtils.createTestMedia() + private val UPLOADED_MEDIA = MediaTestUtils.createTestMedia().apply { fileName = "" filePath = "" url = "" @@ -122,7 +122,7 @@ 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.createTestMedia())) advanceUntilIdle() verify(mediaFilesRepository).uploadMedia(any(), any()) @@ -133,9 +133,9 @@ 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.createTestMedia()))) - worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaModel(0, 0))) + worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createTestMedia())) advanceUntilIdle() verify(notificationHandler).setProgress(0.5f) @@ -153,7 +153,7 @@ 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.createTestMedia())) advanceUntilIdle() assertThat(eventsList).contains(UploadFailed(REMOTE_PRODUCT_ID, TEST_URI, error)) @@ -166,7 +166,7 @@ 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.createTestMedia())) advanceUntilIdle() assertThat(eventsList).contains(ProductUploadsCompleted(REMOTE_PRODUCT_ID)) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt index 5c419938c7cc..874b77f032f8 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt @@ -29,7 +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.media.MediaTestUtils import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.FAILED import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.UPLOADED import org.wordpress.android.fluxc.store.MediaStore.MediaErrorType @@ -80,7 +80,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.createTestMedia() eventsFlow.tryEmit( Event.MediaUploadEvent.FetchSucceeded( REMOTE_PRODUCT_ID, @@ -129,7 +129,7 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { assertThat(successfulUpload.uploadState).isEqualTo(UPLOADED.toString()) } - val mediaModel = MediaModel(0, 0).apply { + val mediaModel = MediaTestUtils.createTestMedia().apply { postId = REMOTE_PRODUCT_ID setUploadState(UPLOADED) } @@ -146,7 +146,7 @@ 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 { + val mediaModel = MediaTestUtils.createTestMedia().apply { postId = REMOTE_PRODUCT_ID fileName = "test" url = "url" @@ -170,7 +170,7 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { val testUri2 = "file:///test2" mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI, testUri2)) - val mediaModel = MediaModel(0, 0).apply { + val mediaModel = MediaTestUtils.createTestMedia().apply { postId = REMOTE_PRODUCT_ID fileName = "test" url = "url" @@ -203,7 +203,7 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { val job = launch { mediaFileUploadHandler.observeSuccessfulUploads(REMOTE_PRODUCT_ID).collect() } - val mediaModel = MediaModel(0, 0).apply { + val mediaModel = MediaTestUtils.createTestMedia().apply { postId = REMOTE_PRODUCT_ID setUploadState(FAILED) } @@ -229,7 +229,7 @@ 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 { + val mediaModel = MediaTestUtils.createTestMedia().apply { postId = REMOTE_PRODUCT_ID setUploadState(FAILED) } @@ -261,7 +261,7 @@ 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 { + val mediaModel = MediaTestUtils.createTestMedia().apply { fileName = "test" url = "url" uploadDate = DateTimeUtils.iso8601FromDate(Date()) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt index a6271befc738..01c3e9905263 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt @@ -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 @@ -1090,7 +1090,7 @@ 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 { + MediaTestUtils.createTestMedia().apply { url = it uploadDate = "2022-09-27 18:00:00.000" } diff --git a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java deleted file mode 100644 index 9839b1f0d466..000000000000 --- a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaTestUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.wordpress.android.fluxc.media; - -import org.wordpress.android.fluxc.model.MediaModel; -import org.wordpress.android.fluxc.utils.MediaUtils; - -public class MediaTestUtils { - public static MediaModel generateMediaFromPath(int localSiteId, long mediaId, String filePath) { - return generateMediaFromPath(localSiteId, mediaId, filePath, null, null, null); - } - - //Temporary hack - private static int sNextId = 1; - - public static MediaModel generateMediaFromPath(int localSiteId, long mediaId, String filePath, - String title, String description, String caption) { - MediaModel media = new MediaModel(localSiteId, mediaId); - media.setId(sNextId++); // Assign unique ID for cache - media.setFilePath(filePath); - media.setFileName(MediaUtils.getFileName(filePath)); - String extension = MediaUtils.getExtension(filePath); - media.setMimeType(MediaUtils.getMimeTypeForExtension(extension)); - media.setTitle(title != null ? title : media.getFileName()); - if (description != null) { - media.setDescription(description); - } - if (caption != null) { - media.setCaption(caption); - } - return media; - } -} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index 23230eeaa753..a91c7c982986 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -86,20 +86,6 @@ public MediaModel() { this.mMarkedLocallyAsFeatured = false; } - /** - * Use when getting an existing media. - */ - public MediaModel( - int localSiteId, - long mediaId) { - this.mLocalSiteId = localSiteId; - this.mMediaId = mediaId; - this.mUrl = ""; - this.mCaption = ""; - this.mDescription = ""; - this.mAlt = ""; - } - /** * Use when converting local uri into a media, and then, to upload a new or update an existing media. */ diff --git a/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt new file mode 100644 index 000000000000..981f57253b6b --- /dev/null +++ b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt @@ -0,0 +1,77 @@ +package org.wordpress.android.fluxc.media + +import org.wordpress.android.fluxc.model.MediaModel +import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState +import org.wordpress.android.fluxc.utils.MimeTypes +import java.util.concurrent.atomic.AtomicInteger + +object MediaTestUtils { + private val nextId = AtomicInteger(1) + private val mimeTypes = MimeTypes() + + @JvmStatic + @JvmOverloads + fun createTestMedia( + localSiteId: Int = 1, + mediaId: Long = 0L, + postId: Long = 0L, + uploadDate: String? = null, + url: String = "", + thumbnailUrl: String? = null, + fileName: String? = null, + filePath: String? = null, + mimeType: String? = null, + title: String? = null, + caption: String = "", + description: String = "", + alt: String = "", + uploadState: MediaUploadState = MediaUploadState.UPLOADED + ): MediaModel { + val media = MediaModel( + localSiteId, + mediaId, + postId, + uploadDate, + url, + thumbnailUrl, + fileName, + mimeType, + title, + caption, + description, + alt, + uploadState + ) + media.id = nextId.getAndIncrement() + if (filePath != null) { + media.filePath = filePath + } + return media + } + + @JvmStatic + @JvmOverloads + fun generateMediaFromPath( + localSiteId: Int, + mediaId: Long, + filePath: String, + title: String? = null, + description: String? = null, + caption: String? = null + ): MediaModel { + val fileName = filePath.substringAfterLast('/') + val extension = fileName.substringAfterLast('.', "") + val mimeType = mimeTypes.getMimeTypeForExtension(extension) + + return createTestMedia( + localSiteId = localSiteId, + mediaId = mediaId, + fileName = fileName, + filePath = filePath, + mimeType = mimeType, + title = title ?: fileName, + description = description ?: "", + caption = caption ?: "" + ) + } +} From b8ea0bcefb750f7c524aa228c6bb947f6341bf62 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 16 Jan 2026 10:22:52 +0100 Subject: [PATCH 08/27] Remove any mentions of WellSql from `MediaModel` and unused WellSql constructor --- .../android/fluxc/media/MediaStoreTest.java | 16 ----- .../android/fluxc/model/MediaModel.java | 65 +++++-------------- 2 files changed, 17 insertions(+), 64 deletions(-) diff --git a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java index 0eb9a527b8c8..858a310d989b 100644 --- a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java +++ b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java @@ -6,25 +6,17 @@ import static org.wordpress.android.fluxc.media.MediaTestUtils.generateMediaFromPath; import static org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId; -import android.content.Context; - -import com.yarolegovich.wellsql.WellSql; - import org.jetbrains.annotations.NotNull; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.wordpress.android.fluxc.Dispatcher; -import org.wordpress.android.fluxc.SingleStoreWellSqlConfigForTests; import org.wordpress.android.fluxc.logging.FakeCrashLogging; import org.wordpress.android.fluxc.model.MediaModel; import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.network.rest.wpapi.media.ApplicationPasswordsMediaRestClient; import org.wordpress.android.fluxc.network.rest.wpcom.media.wpv2.WPComV2MediaRestClient; -import org.wordpress.android.fluxc.persistence.WellSqlConfig; import org.wordpress.android.fluxc.store.MediaCacheOperations; import org.wordpress.android.fluxc.store.MediaIdGenerator; import org.wordpress.android.fluxc.store.MediaLibraryCache; @@ -58,14 +50,6 @@ private static class FakeMediaIdGenerator implements MediaIdGenerator { new FakeMediaIdGenerator() ); - @Before - public void setUp() { - Context context = RuntimeEnvironment.getApplication().getApplicationContext(); - WellSqlConfig config = new SingleStoreWellSqlConfigForTests(context, MediaModel.class); - WellSql.init(config); - config.reset(); - } - @Test public void testGetSiteImages() { final String testVideoPath = "/test/test_video.mp4"; diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index a91c7c982986..4fbd8a0a75f4 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -3,20 +3,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.yarolegovich.wellsql.core.Identifiable; -import com.yarolegovich.wellsql.core.annotation.Column; -import com.yarolegovich.wellsql.core.annotation.PrimaryKey; -import com.yarolegovich.wellsql.core.annotation.Table; - import org.wordpress.android.fluxc.Payload; import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError; import org.wordpress.android.util.StringUtils; import java.io.Serializable; -// WARN: This class is used within WordPress-MediaPicker-Android, do not remove! -@Table -public class MediaModel extends Payload implements Identifiable, Serializable { +public class MediaModel extends Payload implements Serializable { private static final long serialVersionUID = -1396457338496002846L; public enum MediaUploadState { @@ -35,56 +28,34 @@ public static MediaUploadState fromString(@Nullable String stringState) { } } - @PrimaryKey - @Column private int mId; + private int mId; // Associated IDs - @Column private int mLocalSiteId; - @Column private long mMediaId; // The remote ID of the media - @Column private long mPostId; // The remote post ID ('parent') of the media + private int mLocalSiteId; + private long mMediaId; // The remote ID of the media + private long mPostId; // The remote post ID ('parent') of the media // Upload date, ISO 8601-formatted date in UTC - @Nullable @Column private String mUploadDate; + @Nullable private String mUploadDate; // Remote Url's - @NonNull @Column private String mUrl; - @Nullable @Column private String mThumbnailUrl; + @NonNull private String mUrl; + @Nullable private String mThumbnailUrl; // File descriptors - @Nullable @Column private String mFileName; - @Nullable @Column private String mFilePath; - @Nullable @Column private String mMimeType; + @Nullable private String mFileName; + @Nullable private String mFilePath; + @Nullable private String mMimeType; // Descriptive strings - @Nullable @Column private String mTitle; - @NonNull @Column private String mCaption; - @NonNull @Column private String mDescription; - @NonNull @Column private String mAlt; + @Nullable private String mTitle; + @NonNull private String mCaption; + @NonNull private String mDescription; + @NonNull private String mAlt; // Local only - @Nullable @Column private String mUploadState; - @Column private boolean mMarkedLocallyAsFeatured; - - @Deprecated - @SuppressWarnings("DeprecatedIsStillUsed") - public MediaModel() { - this.mId = 0; - this.mLocalSiteId = 0; - this.mMediaId = 0; - this.mPostId = 0; - this.mUploadDate = null; - this.mUrl = ""; - this.mThumbnailUrl = null; - this.mFileName = null; - this.mFilePath = null; - this.mMimeType = null; - this.mTitle = null; - this.mCaption = ""; - this.mDescription = ""; - this.mAlt = ""; - this.mUploadState = null; - this.mMarkedLocallyAsFeatured = false; - } + @Nullable private String mUploadState; + private boolean mMarkedLocallyAsFeatured; /** * Use when converting local uri into a media, and then, to upload a new or update an existing media. @@ -165,12 +136,10 @@ && getMarkedLocallyAsFeatured() == otherMedia.getMarkedLocallyAsFeatured() && StringUtils.equals(getUploadState(), otherMedia.getUploadState()); } - @Override public void setId(int id) { mId = id; } - @Override public int getId() { return mId; } From dabae37e615c73812e73b429f9354e4e45b97c57 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 16 Jan 2026 11:01:58 +0100 Subject: [PATCH 09/27] Remove all unused setters for `MediaModel` Refactor `MediaTestUtils` to use Java-friendly builder pattern --- .../media/ProductImagesUploadWorkerTest.kt | 31 ++-- .../ui/media/MediaFileUploadHandlerTest.kt | 66 +++---- .../details/ProductDetailViewModelTest.kt | 8 +- .../android/fluxc/model/MediaModel.java | 62 +------ .../android/fluxc/media/MediaTestUtils.kt | 161 +++++++++++++----- 5 files changed, 179 insertions(+), 149 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt index 738913a4feda..b79032f07334 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt @@ -45,13 +45,12 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() { companion object { private const val REMOTE_PRODUCT_ID = 1L private const val TEST_URI = "test" - private val FETCHED_MEDIA = MediaTestUtils.createTestMedia() - private val UPLOADED_MEDIA = MediaTestUtils.createTestMedia().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() @@ -122,7 +121,9 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() { val job = launch { worker.events.toList(eventsList) } - worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createTestMedia())) + worker.enqueueWork( + Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build()) + ) advanceUntilIdle() verify(mediaFilesRepository).uploadMedia(any(), any()) @@ -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(MediaTestUtils.createTestMedia()))) + .thenReturn(flowOf(UploadProgress(0.5f), UploadSuccess(MediaTestUtils.createRemoteTestMedia().build()))) - worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createTestMedia())) + worker.enqueueWork( + Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build()) + ) advanceUntilIdle() verify(notificationHandler).setProgress(0.5f) @@ -153,7 +156,9 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() { val job = launch { worker.events.toList(eventsList) } - worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createTestMedia())) + worker.enqueueWork( + Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build()) + ) advanceUntilIdle() assertThat(eventsList).contains(UploadFailed(REMOTE_PRODUCT_ID, TEST_URI, error)) @@ -166,7 +171,9 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() { val job = launch { worker.events.toList(eventsList) } - worker.enqueueWork(Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createTestMedia())) + worker.enqueueWork( + Work.UploadMedia(REMOTE_PRODUCT_ID, TEST_URI, MediaTestUtils.createRemoteTestMedia().build()) + ) advanceUntilIdle() assertThat(eventsList).contains(ProductUploadsCompleted(REMOTE_PRODUCT_ID)) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt index 874b77f032f8..d5ed6f7f4b30 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt @@ -80,7 +80,7 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { fun `when media is fetched, then start uploading it`() = testBlocking { mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI)) - val fetchedMedia = MediaTestUtils.createTestMedia() + val fetchedMedia = MediaTestUtils.createRemoteTestMedia().build() eventsFlow.tryEmit( Event.MediaUploadEvent.FetchSucceeded( REMOTE_PRODUCT_ID, @@ -129,10 +129,10 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { assertThat(successfulUpload.uploadState).isEqualTo(UPLOADED.toString()) } - val mediaModel = MediaTestUtils.createTestMedia().apply { - postId = REMOTE_PRODUCT_ID - setUploadState(UPLOADED) - } + val mediaModel = MediaTestUtils.createRemoteTestMedia() + .postId(REMOTE_PRODUCT_ID) + .uploadState(UPLOADED) + .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadSucceeded( REMOTE_PRODUCT_ID, @@ -146,13 +146,13 @@ 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 = MediaTestUtils.createTestMedia().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())) + .uploadState(UPLOADED) + .postId(REMOTE_PRODUCT_ID) + .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadSucceeded( REMOTE_PRODUCT_ID, @@ -170,13 +170,13 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { val testUri2 = "file:///test2" mediaFileUploadHandler.enqueueUpload(REMOTE_PRODUCT_ID, listOf(TEST_URI, testUri2)) - val mediaModel = MediaTestUtils.createTestMedia().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())) + .uploadState(UPLOADED) + .postId(REMOTE_PRODUCT_ID) + .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadSucceeded( REMOTE_PRODUCT_ID, @@ -203,10 +203,10 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { val job = launch { mediaFileUploadHandler.observeSuccessfulUploads(REMOTE_PRODUCT_ID).collect() } - val mediaModel = MediaTestUtils.createTestMedia().apply { - postId = REMOTE_PRODUCT_ID - setUploadState(FAILED) - } + val mediaModel = MediaTestUtils.createRemoteTestMedia() + .postId(REMOTE_PRODUCT_ID) + .uploadState(FAILED) + .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadFailed( @@ -229,10 +229,10 @@ 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 = MediaTestUtils.createTestMedia().apply { - postId = REMOTE_PRODUCT_ID - setUploadState(FAILED) - } + val mediaModel = MediaTestUtils.createRemoteTestMedia() + .postId(REMOTE_PRODUCT_ID) + .uploadState(FAILED) + .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadFailed( REMOTE_PRODUCT_ID, @@ -261,12 +261,12 @@ 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 = MediaTestUtils.createTestMedia().apply { - fileName = "test" - url = "url" - uploadDate = DateTimeUtils.iso8601FromDate(Date()) - setUploadState(UPLOADED) - } + val mediaModel = MediaTestUtils.createRemoteTestMedia() + .fileName("test") + .url("url") + .uploadDate(DateTimeUtils.iso8601FromDate(Date())) + .uploadState(UPLOADED) + .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadSucceeded( ProductDetailViewModel.DEFAULT_ADD_NEW_PRODUCT_ID, diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt index 01c3e9905263..d898ce9b9081 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/details/ProductDetailViewModelTest.kt @@ -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 { - MediaTestUtils.createTestMedia().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() } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index 4fbd8a0a75f4..93ec4909c930 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -10,8 +10,6 @@ import java.io.Serializable; public class MediaModel extends Payload implements Serializable { - private static final long serialVersionUID = -1396457338496002846L; - public enum MediaUploadState { QUEUED, UPLOADING, DELETING, DELETED, FAILED, UPLOADED; @@ -36,22 +34,22 @@ public static MediaUploadState fromString(@Nullable String stringState) { private long mPostId; // The remote post ID ('parent') of the media // Upload date, ISO 8601-formatted date in UTC - @Nullable private String mUploadDate; + @Nullable private final String mUploadDate; // Remote Url's - @NonNull private String mUrl; + @NonNull private final String mUrl; @Nullable private String mThumbnailUrl; // File descriptors - @Nullable private String mFileName; + @Nullable private final String mFileName; @Nullable private String mFilePath; - @Nullable private String mMimeType; + @Nullable private final String mMimeType; // Descriptive strings - @Nullable private String mTitle; - @NonNull private String mCaption; - @NonNull private String mDescription; - @NonNull private String mAlt; + @Nullable private final String mTitle; + @NonNull private final String mCaption; + @NonNull private final String mDescription; + @NonNull private final String mAlt; // Local only @Nullable private String mUploadState; @@ -168,100 +166,56 @@ public long getPostId() { return mPostId; } - public void setUploadDate(@Nullable String uploadDate) { - mUploadDate = uploadDate; - } - @Nullable public String getUploadDate() { return mUploadDate; } - public void setUrl(@NonNull String url) { - mUrl = url; - } - @NonNull public String getUrl() { return mUrl; } - public void setThumbnailUrl(@Nullable String thumbnailUrl) { - mThumbnailUrl = thumbnailUrl; - } - @Nullable public String getThumbnailUrl() { return mThumbnailUrl; } - public void setFileName(@Nullable String fileName) { - mFileName = fileName; - } - @Nullable public String getFileName() { return mFileName; } - public void setFilePath(@Nullable String filePath) { - mFilePath = filePath; - } - @Nullable public String getFilePath() { return mFilePath; } - public void setMimeType(@Nullable String mimeType) { - mMimeType = mimeType; - } - @Nullable public String getMimeType() { return mMimeType; } - public void setTitle(@Nullable String title) { - mTitle = title; - } - @Nullable public String getTitle() { return mTitle; } - public void setCaption(@NonNull String caption) { - mCaption = caption; - } - @NonNull public String getCaption() { return mCaption; } - public void setDescription(@NonNull String description) { - mDescription = description; - } - @NonNull public String getDescription() { return mDescription; } - public void setAlt(@NonNull String alt) { - mAlt = alt; - } - @NonNull public String getAlt() { return mAlt; } - public void setUploadState(@Nullable String uploadState) { - mUploadState = uploadState; - } - public void setUploadState(@NonNull MediaUploadState uploadState) { mUploadState = uploadState.toString(); } diff --git a/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt index 981f57253b6b..febf42b16778 100644 --- a/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt +++ b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt @@ -10,43 +10,102 @@ object MediaTestUtils { private val mimeTypes = MimeTypes() @JvmStatic - @JvmOverloads - fun createTestMedia( - localSiteId: Int = 1, - mediaId: Long = 0L, - postId: Long = 0L, - uploadDate: String? = null, - url: String = "", - thumbnailUrl: String? = null, - fileName: String? = null, - filePath: String? = null, - mimeType: String? = null, - title: String? = null, - caption: String = "", - description: String = "", - alt: String = "", - uploadState: MediaUploadState = MediaUploadState.UPLOADED - ): MediaModel { - val media = MediaModel( - localSiteId, - mediaId, - postId, - uploadDate, - url, - thumbnailUrl, - fileName, - mimeType, - title, - caption, - description, - alt, - uploadState - ) - media.id = nextId.getAndIncrement() - if (filePath != null) { - media.filePath = filePath + fun createLocalTestMedia(): LocalTestMediaBuilder { + return LocalTestMediaBuilder() + } + + @JvmStatic + fun createRemoteTestMedia(): RemoteTestMediaBuilder { + return RemoteTestMediaBuilder() + } + + class LocalTestMediaBuilder internal constructor() { + private var localSiteId: Int = 1 + private var mediaId: Long = 0L + private var postId: Long = 0L + private var uploadDate: String? = "2024-01-01T00:00:00+00:00" + private var fileName: String? = "test-image.jpg" + private var filePath: String? = "/test/test-image.jpg" + private var mimeType: String? = "image/jpeg" + private var title: String? = "Test Image" + private var uploadState: MediaUploadState = MediaUploadState.UPLOADED + + fun localSiteId(value: Int) = apply { this.localSiteId = value } + fun mediaId(value: Long) = apply { this.mediaId = value } + fun postId(value: Long) = apply { this.postId = value } + fun uploadDate(value: String?) = apply { this.uploadDate = value } + fun fileName(value: String?) = apply { this.fileName = value } + fun filePath(value: String?) = apply { this.filePath = value } + fun mimeType(value: String?) = apply { this.mimeType = value } + fun title(value: String?) = apply { this.title = value } + fun uploadState(value: MediaUploadState) = apply { this.uploadState = value } + + fun build(): MediaModel { + val media = MediaModel( + this.localSiteId, + this.uploadDate, + this.fileName, + this.filePath, + this.mimeType, + this.title, + this.uploadState + ).apply { + setMediaId(this@LocalTestMediaBuilder.mediaId) + setPostId(this@LocalTestMediaBuilder.postId) + } + media.id = nextId.getAndIncrement() + return media + } + } + + class RemoteTestMediaBuilder internal constructor() { + private var localSiteId: Int = 1 + private var mediaId: Long = 1L + private var postId: Long = 0L + private var uploadDate: String? = "2024-01-01T00:00:00+00:00" + private var url: String = "https://example.com/test-image.jpg" + private var thumbnailUrl: String? = "https://example.com/test-image-thumb.jpg" + private var fileName: String? = "test-image.jpg" + private var mimeType: String? = "image/jpeg" + private var title: String? = "Test Image" + private var caption: String = "" + private var description: String = "" + private var alt: String = "" + private var uploadState: MediaUploadState = MediaUploadState.UPLOADED + + fun localSiteId(value: Int) = apply { this.localSiteId = value } + fun mediaId(value: Long) = apply { this.mediaId = value } + fun postId(value: Long) = apply { this.postId = value } + fun uploadDate(value: String?) = apply { this.uploadDate = value } + fun url(value: String) = apply { this.url = value } + fun thumbnailUrl(value: String?) = apply { this.thumbnailUrl = value } + fun fileName(value: String?) = apply { this.fileName = value } + fun mimeType(value: String?) = apply { this.mimeType = value } + fun title(value: String?) = apply { this.title = value } + fun caption(value: String) = apply { this.caption = value } + fun description(value: String) = apply { this.description = value } + fun alt(value: String) = apply { this.alt = value } + fun uploadState(value: MediaUploadState) = apply { this.uploadState = value } + + fun build(): MediaModel { + val media = MediaModel( + this.localSiteId, + this.mediaId, + this.postId, + this.uploadDate, + this.url, + this.thumbnailUrl, + this.fileName, + this.mimeType, + this.title, + this.caption, + this.description, + this.alt, + this.uploadState + ) + media.id = nextId.getAndIncrement() + return media } - return media } @JvmStatic @@ -63,15 +122,25 @@ object MediaTestUtils { val extension = fileName.substringAfterLast('.', "") val mimeType = mimeTypes.getMimeTypeForExtension(extension) - return createTestMedia( - localSiteId = localSiteId, - mediaId = mediaId, - fileName = fileName, - filePath = filePath, - mimeType = mimeType, - title = title ?: fileName, - description = description ?: "", - caption = caption ?: "" - ) + return if (description.isNullOrEmpty() && caption.isNullOrEmpty()) { + createLocalTestMedia() + .localSiteId(localSiteId) + .mediaId(mediaId) + .fileName(fileName) + .filePath(filePath) + .mimeType(mimeType) + .title(title ?: fileName) + .build() + } else { + createRemoteTestMedia() + .localSiteId(localSiteId) + .mediaId(mediaId) + .fileName(fileName) + .mimeType(mimeType) + .title(title ?: fileName) + .description(description ?: "") + .caption(caption ?: "") + .build() + } } } From db0b3f8ab3b32d354a492d38b727c2a8f717511a Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Mon, 19 Jan 2026 11:31:23 +0100 Subject: [PATCH 10/27] Rename `MediaFileRepository#fetchMedia` to `getLocalMedia` `fetch` was for me very misleading as, at least for me, it strongly indicates a remote source (like backend/API). This method only calls `FileUploadUtils.mediaModelFromLocalUri` which is purely local operation, to prepare a file for upload. --- .../com/woocommerce/android/media/MediaFilesRepository.kt | 4 ++-- .../woocommerce/android/media/ProductImagesUploadWorker.kt | 4 +--- .../android/media/ProductImagesUploadWorkerTest.kt | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/MediaFilesRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/MediaFilesRepository.kt index c300d34c01d4..1a8f961a939c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/MediaFilesRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/MediaFilesRepository.kt @@ -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, @@ -120,7 +120,7 @@ class MediaFilesRepository @Inject constructor( fun uploadFile(localUri: String): Flow { return flow { - val mediaModel = fetchMedia(localUri) + val mediaModel = getLocalMedia(localUri) if (mediaModel == null) { WooLog.w(T.MEDIA, "MediaFilesRepository > null media") diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/ProductImagesUploadWorker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/ProductImagesUploadWorker.kt index 1a604bb58fac..600ecd3ac508 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/ProductImagesUploadWorker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/ProductImagesUploadWorker.kt @@ -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 @@ -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") diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt index b79032f07334..ae1979250626 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/media/ProductImagesUploadWorkerTest.kt @@ -57,7 +57,7 @@ class ProductImagesUploadWorkerTest : BaseUnitTest() { 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() @@ -110,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() } From 9738010f84d7fb9cfbe43bb66a4310dd34f56f4f Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Mon, 19 Jan 2026 11:32:23 +0100 Subject: [PATCH 11/27] Remove unused methods/obsolete comments `MediaStore` can't conflict with FluxC anymore since 41b4c5d --- .../wordpress/android/fluxc/store/MediaStore.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java index 48ccad39bab6..ba154becfc82 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java @@ -14,7 +14,6 @@ import org.wordpress.android.fluxc.annotations.action.IAction; import org.wordpress.android.fluxc.logging.FluxCCrashLogger; import org.wordpress.android.fluxc.model.MediaModel; -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState; import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError; import org.wordpress.android.fluxc.network.rest.wpapi.applicationpasswords.ApplicationPasswordsConfiguration; @@ -39,19 +38,8 @@ import javax.inject.Inject; import javax.inject.Singleton; -// WARN: This class is used within WordPress-MediaPicker-Android, do not remove! @Singleton public class MediaStore extends Store { - public static final List NOT_DELETED_STATES = new ArrayList<>(); - - static { - NOT_DELETED_STATES.add(MediaUploadState.DELETING.toString()); - NOT_DELETED_STATES.add(MediaUploadState.FAILED.toString()); - NOT_DELETED_STATES.add(MediaUploadState.QUEUED.toString()); - NOT_DELETED_STATES.add(MediaUploadState.UPLOADED.toString()); - NOT_DELETED_STATES.add(MediaUploadState.UPLOADING.toString()); - } - public static class MediaPayload extends Payload { @NonNull public SiteModel site; @Nullable public MediaModel media; @@ -285,7 +273,6 @@ public OnMediaChanged( } } - // WARN: This class is used within WordPress-MediaPicker-Android, do not remove! public static class OnMediaListFetched extends OnChanged { @NonNull public SiteModel site; public boolean canLoadMore; From cfe92ffeea1fae73681f4e4cb541674bc3d9f8d2 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Mon, 19 Jan 2026 12:26:57 +0100 Subject: [PATCH 12/27] Remove another set of unused `MediaModel` properties and API wrappers --- .../fluxc/model/WCProductImageModel.kt | 13 --- .../android/fluxc/model/MediaModel.java | 31 +----- .../wpapi/media/BaseWPV2MediaRestClient.kt | 1 - .../rest/wpapi/media/MediaWPRESTResponse.kt | 6 +- .../wpapi/media/WPRestUploadRequestBody.kt | 2 - .../rest/wpcom/media/MediaResponseUtils.kt | 52 --------- .../wpcom/media/MediaWPComRestResponse.java | 40 ------- .../wpcom/media/RestUploadRequestBody.java | 103 ------------------ .../fluxc/store/MediaLibraryCacheTest.kt | 2 - .../android/fluxc/media/MediaTestUtils.kt | 4 - 10 files changed, 4 insertions(+), 250 deletions(-) delete mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaResponseUtils.kt delete mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaWPComRestResponse.java delete mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/RestUploadRequestBody.java diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCProductImageModel.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCProductImageModel.kt index 30fa1a0dc213..b8e4cd4bba67 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCProductImageModel.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCProductImageModel.kt @@ -1,7 +1,6 @@ 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 = "" @@ -9,18 +8,6 @@ class WCProductImageModel(val id: Long) { 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) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index 93ec4909c930..c3536159487d 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -38,7 +38,6 @@ public static MediaUploadState fromString(@Nullable String stringState) { // Remote Url's @NonNull private final String mUrl; - @Nullable private String mThumbnailUrl; // File descriptors @Nullable private final String mFileName; @@ -49,11 +48,9 @@ public static MediaUploadState fromString(@Nullable String stringState) { @Nullable private final String mTitle; @NonNull private final String mCaption; @NonNull private final String mDescription; - @NonNull private final String mAlt; // Local only @Nullable private String mUploadState; - private boolean mMarkedLocallyAsFeatured; /** * Use when converting local uri into a media, and then, to upload a new or update an existing media. @@ -75,36 +72,34 @@ public MediaModel( this.mTitle = title; this.mCaption = ""; this.mDescription = ""; - this.mAlt = ""; this.mUploadState = uploadState != null ? uploadState.toString() : null; } + /** + * Used for receiving media from the remote + */ public MediaModel( int localSiteId, long mediaId, long postId, @Nullable String uploadDate, @NonNull String url, - @Nullable String thumbnailUrl, @Nullable String fileName, @Nullable String mimeType, @Nullable String title, @NonNull String caption, @NonNull String description, - @NonNull String alt, @NonNull MediaUploadState uploadState) { this.mLocalSiteId = localSiteId; this.mMediaId = mediaId; this.mPostId = postId; this.mUploadDate = uploadDate; this.mUrl = url; - this.mThumbnailUrl = thumbnailUrl; this.mFileName = fileName; this.mMimeType = mimeType; this.mTitle = title; this.mCaption = caption; this.mDescription = description; - this.mAlt = alt; this.mUploadState = uploadState.toString(); } @@ -120,17 +115,14 @@ public boolean equals(@Nullable Object other) { && getLocalSiteId() == otherMedia.getLocalSiteId() && getMediaId() == otherMedia.getMediaId() && getPostId() == otherMedia.getPostId() - && getMarkedLocallyAsFeatured() == otherMedia.getMarkedLocallyAsFeatured() && StringUtils.equals(getUploadDate(), otherMedia.getUploadDate()) && StringUtils.equals(getUrl(), otherMedia.getUrl()) - && StringUtils.equals(getThumbnailUrl(), otherMedia.getThumbnailUrl()) && StringUtils.equals(getFileName(), otherMedia.getFileName()) && StringUtils.equals(getFilePath(), otherMedia.getFilePath()) && StringUtils.equals(getMimeType(), otherMedia.getMimeType()) && StringUtils.equals(getTitle(), otherMedia.getTitle()) && StringUtils.equals(getDescription(), otherMedia.getDescription()) && StringUtils.equals(getCaption(), otherMedia.getCaption()) - && StringUtils.equals(getAlt(), otherMedia.getAlt()) && StringUtils.equals(getUploadState(), otherMedia.getUploadState()); } @@ -176,11 +168,6 @@ public String getUrl() { return mUrl; } - @Nullable - public String getThumbnailUrl() { - return mThumbnailUrl; - } - @Nullable public String getFileName() { return mFileName; @@ -211,11 +198,6 @@ public String getDescription() { return mDescription; } - @NonNull - public String getAlt() { - return mAlt; - } - public void setUploadState(@NonNull MediaUploadState uploadState) { mUploadState = uploadState.toString(); } @@ -225,11 +207,4 @@ public String getUploadState() { return mUploadState; } - public boolean getMarkedLocallyAsFeatured() { - return mMarkedLocallyAsFeatured; - } - - public void setMarkedLocallyAsFeatured(boolean markedLocallyAsFeatured) { - mMarkedLocallyAsFeatured = markedLocallyAsFeatured; - } } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt index 199695bd619c..3ecf0fd3058a 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart import okhttp3.Call diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt index 0c3fa40a7085..81f86eb99772 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt @@ -7,7 +7,6 @@ import org.apache.commons.text.StringEscapeUtils import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState import org.wordpress.android.fluxc.network.rest.JsonObjectOrNull -import org.wordpress.android.fluxc.network.rest.wpcom.media.MediaWPComRestResponse import org.wordpress.android.util.DateTimeUtils import java.text.SimpleDateFormat import java.util.Locale @@ -20,7 +19,6 @@ data class MediaWPRESTResponse( val post: Long? = null, val description: Attribute, val caption: Attribute, - @SerializedName("alt_text") val altText: String, @SerializedName("mime_type") val mimeType: String, @SerializedName("media_details") val mediaDetails: MediaDetails?, @SerializedName("source_url") val sourceURL: String? @@ -51,14 +49,12 @@ fun MediaWPRESTResponse.toMediaModel(localSiteId: Int) = MediaModel( SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT).parse(dateGmt) ), sourceURL.orEmpty(), - mediaDetails?.sizes?.thumbnail?.sourceURL, mediaDetails?.file, mimeType, StringEscapeUtils.unescapeHtml4(title.rendered), StringEscapeUtils.unescapeHtml4(caption.rendered), StringEscapeUtils.unescapeHtml4(description.rendered), - StringEscapeUtils.unescapeHtml4(altText), - if (MediaWPComRestResponse.DELETED_STATUS == status) { + if (status == "deleted") { MediaUploadState.DELETED } else { MediaUploadState.UPLOADED diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/WPRestUploadRequestBody.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/WPRestUploadRequestBody.kt index 3263336cadfa..2f970d9d7214 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/WPRestUploadRequestBody.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/WPRestUploadRequestBody.kt @@ -17,7 +17,6 @@ private const val FILE_FORM_KEY = "file" private const val TITLE_FORM_KEY = "title" private const val DESCRIPTION_FORM_KEY = "description" private const val CAPTION_FORM_KEY = "caption" -private const val ALT_FORM_KEY = "alt_text" private const val POST_ID_FORM_KEY = "post" class WPRestUploadRequestBody( @@ -44,7 +43,6 @@ class WPRestUploadRequestBody( .addParamIfNotEmpty(TITLE_FORM_KEY, media.title) .addParamIfNotEmpty(DESCRIPTION_FORM_KEY, media.description) .addParamIfNotEmpty(CAPTION_FORM_KEY, media.caption) - .addParamIfNotEmpty(ALT_FORM_KEY, media.alt) .addParamIfNotEmpty(POST_ID_FORM_KEY, media.postId.takeIf { it > 0L }?.toString()) val filePath = media.filePath diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaResponseUtils.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaResponseUtils.kt deleted file mode 100644 index 37b5ead6f05f..000000000000 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaResponseUtils.kt +++ /dev/null @@ -1,52 +0,0 @@ -package org.wordpress.android.fluxc.network.rest.wpcom.media - -import android.text.TextUtils -import org.apache.commons.text.StringEscapeUtils -import org.wordpress.android.fluxc.model.MediaModel -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState -import org.wordpress.android.fluxc.network.rest.wpcom.media.MediaWPComRestResponse.MultipleMediaResponse -import javax.inject.Inject - -class MediaResponseUtils -@Inject constructor() { - /** - * Creates a [MediaModel] list from a WP.com REST response to a request for all media. - */ - fun getMediaListFromRestResponse( - from: MultipleMediaResponse, - localSiteId: Int - ): List { - return from.media.mapNotNull { - getMediaFromRestResponse(it, localSiteId) - } - } - - /** - * Creates a [MediaModel] from a WP.com REST response to a fetch request. - */ - fun getMediaFromRestResponse(from: MediaWPComRestResponse, siteId: Int) = MediaModel( - siteId, - from.ID, - from.post_ID, - from.date, - from.URL, - from.thumbnails?.let { - if (!TextUtils.isEmpty(it.fmt_std)) { - it.fmt_std - } else { - it.thumbnail - } - }, - from.file, - from.mime_type, - StringEscapeUtils.unescapeHtml4(from.title), - StringEscapeUtils.unescapeHtml4(from.caption), - StringEscapeUtils.unescapeHtml4(from.description), - StringEscapeUtils.unescapeHtml4(from.alt), - if (MediaWPComRestResponse.DELETED_STATUS == from.status) { - MediaUploadState.DELETED - } else { - MediaUploadState.UPLOADED - } - ) -} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaWPComRestResponse.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaWPComRestResponse.java deleted file mode 100644 index acb1ee9547d3..000000000000 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/MediaWPComRestResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.wordpress.android.fluxc.network.rest.wpcom.media; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.wordpress.android.fluxc.network.Response; - -import java.util.List; - -/** - * Response to GET request for media items - *

- * @see doc - */ -@SuppressWarnings("NotNullFieldNotInitialized") -public class MediaWPComRestResponse implements Response { - public static final String DELETED_STATUS = "deleted"; - - public static class MultipleMediaResponse { - @NonNull public List media; - } - - public static class Thumbnails { - @Nullable public String thumbnail; - @Nullable public String fmt_std; - } - - public long ID; - @NonNull public String date; - public long post_ID; - @NonNull public String URL; - @NonNull public String file; - @NonNull public String mime_type; - @NonNull public String title; - @NonNull public String caption; - @NonNull public String description; - @NonNull public String alt; - @Nullable public Thumbnails thumbnails; - @Nullable public String status; -} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/RestUploadRequestBody.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/RestUploadRequestBody.java deleted file mode 100644 index f31a8022ba3d..000000000000 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/media/RestUploadRequestBody.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.wordpress.android.fluxc.network.rest.wpcom.media; - -import androidx.annotation.NonNull; - -import org.wordpress.android.fluxc.model.MediaModel; -import org.wordpress.android.fluxc.network.BaseUploadRequestBody; -import org.wordpress.android.util.AppLog; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Map; - -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.RequestBody; -import okio.BufferedSink; -import okio.Okio; - -/** - * Wrapper for {@link MultipartBody} that reports upload progress as body data is written. - *

- * A {@link ProgressListener} is required, use {@link MultipartBody} if progress is not needed. - *

- * @see doc - */ -public class RestUploadRequestBody extends BaseUploadRequestBody { - private static final String MEDIA_DATA_KEY = "media[0]"; - private static final String MEDIA_ATTRIBUTES_KEY = "attrs[0]"; - private static final String MEDIA_PARAM_FORMAT = MEDIA_ATTRIBUTES_KEY + "[%s]"; - - @NonNull private final MultipartBody mMultipartBody; - - public RestUploadRequestBody( - @NonNull MediaModel media, - @NonNull Map params, - @NonNull ProgressListener listener) { - super(media, listener); - mMultipartBody = buildMultipartBody(params); - } - - @Override - protected float getProgress(long bytesWritten) { - return (float) bytesWritten / contentLength(); - } - - @Override - public long contentLength() { - try { - return mMultipartBody.contentLength(); - } catch (IOException e) { - AppLog.w(AppLog.T.MEDIA, "Error determining mMultipartBody content length: " + e); - } - return -1L; - } - - @NonNull - @Override - public MediaType contentType() { - return mMultipartBody.contentType(); - } - - @Override - public void writeTo(@NonNull BufferedSink sink) throws IOException { - CountingSink countingSink = new CountingSink(sink); - BufferedSink bufferedSink = Okio.buffer(countingSink); - mMultipartBody.writeTo(bufferedSink); - bufferedSink.flush(); - } - - @NonNull - @SuppressWarnings("deprecation") - private MultipartBody buildMultipartBody(@NonNull Map params) { - MediaModel media = getMedia(); - MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM); - - // add media attributes - for (String key : params.keySet()) { - Object value = params.get(key); - if (value != null) { - builder.addFormDataPart(String.format(MEDIA_PARAM_FORMAT, key), value.toString()); - } - } - - // add media file data - String filePath = media.getFilePath(); - String mimeType = media.getMimeType(); - if (filePath != null && mimeType != null) { - File mediaFile = new File(filePath); - RequestBody body = RequestBody.create(MediaType.parse(mimeType), mediaFile); - String fileName = media.getFileName(); - try { - fileName = URLEncoder.encode(media.getFileName(), "UTF-8"); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - builder.addFormDataPart(MEDIA_DATA_KEY, fileName, body); - } - - return builder.build(); - } -} diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index 7de4d6474a62..09feb09c9235 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -144,13 +144,11 @@ class MediaLibraryCacheTest { 0L, // postId null, // uploadDate "https://example.com/$fileName", // url - null, // thumbnailUrl fileName, // fileName "image/jpeg", // mimeType fileName, // title "", // caption "", // description - "", // alt MediaModel.MediaUploadState.UPLOADED // uploadState ) } diff --git a/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt index febf42b16778..cf9b1e4af958 100644 --- a/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt +++ b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt @@ -64,7 +64,6 @@ object MediaTestUtils { private var postId: Long = 0L private var uploadDate: String? = "2024-01-01T00:00:00+00:00" private var url: String = "https://example.com/test-image.jpg" - private var thumbnailUrl: String? = "https://example.com/test-image-thumb.jpg" private var fileName: String? = "test-image.jpg" private var mimeType: String? = "image/jpeg" private var title: String? = "Test Image" @@ -78,7 +77,6 @@ object MediaTestUtils { fun postId(value: Long) = apply { this.postId = value } fun uploadDate(value: String?) = apply { this.uploadDate = value } fun url(value: String) = apply { this.url = value } - fun thumbnailUrl(value: String?) = apply { this.thumbnailUrl = value } fun fileName(value: String?) = apply { this.fileName = value } fun mimeType(value: String?) = apply { this.mimeType = value } fun title(value: String?) = apply { this.title = value } @@ -94,13 +92,11 @@ object MediaTestUtils { this.postId, this.uploadDate, this.url, - this.thumbnailUrl, this.fileName, this.mimeType, this.title, this.caption, this.description, - this.alt, this.uploadState ) media.id = nextId.getAndIncrement() From 521e2e44ec1c2412791574a9d0d108b2b814b1e8 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Tue, 20 Jan 2026 16:14:05 +0100 Subject: [PATCH 13/27] Remove `MediaModel#mUploadState` property After all of the clean ups it came out clearly, that `mUploadState` isn't really used in any meaningful way in the Woo app. The progress and state of uploads is tracked via different mechanisms. --- .../android/media/FileUploadUtils.kt | 1 - .../ui/media/MediaFileUploadHandlerTest.kt | 11 +-------- .../android/fluxc/model/MediaModel.java | 24 +++---------------- .../wpapi/media/BaseWPV2MediaRestClient.kt | 2 -- .../rest/wpapi/media/MediaWPRESTResponse.kt | 8 +------ .../fluxc/store/MediaLibraryCacheTest.kt | 4 ++-- .../android/fluxc/media/MediaTestUtils.kt | 9 +------ 7 files changed, 8 insertions(+), 51 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt index 05d68c67f982..7d37030f381f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt @@ -85,7 +85,6 @@ object FileUploadUtils { path, mimeType, filenameWithExtension, - null ) val instantiatedMedia = mediaStore.instantiateMediaModel(media) return if (instantiatedMedia != null) { diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt index d5ed6f7f4b30..a65fead5025f 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/media/MediaFileUploadHandlerTest.kt @@ -30,8 +30,6 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.wordpress.android.fluxc.media.MediaTestUtils -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.FAILED -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.UPLOADED 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 @@ -125,13 +123,11 @@ 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 = MediaTestUtils.createRemoteTestMedia() .postId(REMOTE_PRODUCT_ID) - .uploadState(UPLOADED) .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadSucceeded( @@ -150,7 +146,6 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { .fileName("test") .url("url") .uploadDate(DateTimeUtils.iso8601FromDate(Date())) - .uploadState(UPLOADED) .postId(REMOTE_PRODUCT_ID) .build() eventsFlow.tryEmit( @@ -174,7 +169,6 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { .fileName("test") .url("url") .uploadDate(DateTimeUtils.iso8601FromDate(Date())) - .uploadState(UPLOADED) .postId(REMOTE_PRODUCT_ID) .build() eventsFlow.tryEmit( @@ -205,7 +199,6 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { val mediaModel = MediaTestUtils.createRemoteTestMedia() .postId(REMOTE_PRODUCT_ID) - .uploadState(FAILED) .build() eventsFlow.tryEmit( @@ -231,7 +224,6 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { val mediaModel = MediaTestUtils.createRemoteTestMedia() .postId(REMOTE_PRODUCT_ID) - .uploadState(FAILED) .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadFailed( @@ -265,7 +257,6 @@ class MediaFileUploadHandlerTest : BaseUnitTest() { .fileName("test") .url("url") .uploadDate(DateTimeUtils.iso8601FromDate(Date())) - .uploadState(UPLOADED) .build() eventsFlow.tryEmit( Event.MediaUploadEvent.UploadSucceeded( diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index c3536159487d..76d557edb52e 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -49,9 +49,6 @@ public static MediaUploadState fromString(@Nullable String stringState) { @NonNull private final String mCaption; @NonNull private final String mDescription; - // Local only - @Nullable private String mUploadState; - /** * Use when converting local uri into a media, and then, to upload a new or update an existing media. */ @@ -61,8 +58,7 @@ public MediaModel( @Nullable String fileName, @Nullable String filePath, @Nullable String mimeType, - @Nullable String title, - @Nullable MediaUploadState uploadState) { + @Nullable String title) { this.mLocalSiteId = localSiteId; this.mUploadDate = uploadDate; this.mUrl = ""; @@ -72,7 +68,6 @@ public MediaModel( this.mTitle = title; this.mCaption = ""; this.mDescription = ""; - this.mUploadState = uploadState != null ? uploadState.toString() : null; } /** @@ -88,8 +83,7 @@ public MediaModel( @Nullable String mimeType, @Nullable String title, @NonNull String caption, - @NonNull String description, - @NonNull MediaUploadState uploadState) { + @NonNull String description) { this.mLocalSiteId = localSiteId; this.mMediaId = mediaId; this.mPostId = postId; @@ -100,7 +94,6 @@ public MediaModel( this.mTitle = title; this.mCaption = caption; this.mDescription = description; - this.mUploadState = uploadState.toString(); } @Override @@ -122,8 +115,7 @@ && getPostId() == otherMedia.getPostId() && StringUtils.equals(getMimeType(), otherMedia.getMimeType()) && StringUtils.equals(getTitle(), otherMedia.getTitle()) && StringUtils.equals(getDescription(), otherMedia.getDescription()) - && StringUtils.equals(getCaption(), otherMedia.getCaption()) - && StringUtils.equals(getUploadState(), otherMedia.getUploadState()); + && StringUtils.equals(getCaption(), otherMedia.getCaption()); } public void setId(int id) { @@ -197,14 +189,4 @@ public String getCaption() { public String getDescription() { return mDescription; } - - public void setUploadState(@NonNull MediaUploadState uploadState) { - mUploadState = uploadState.toString(); - } - - @Nullable - public String getUploadState() { - return mUploadState; - } - } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt index 3ecf0fd3058a..f54c010ee1a4 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/BaseWPV2MediaRestClient.kt @@ -23,7 +23,6 @@ import org.wordpress.android.fluxc.annotations.endpoint.WPAPIEndpoint import org.wordpress.android.fluxc.generated.MediaActionBuilder import org.wordpress.android.fluxc.generated.endpoint.WPAPI import org.wordpress.android.fluxc.model.MediaModel -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.FAILED import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.network.rest.wpapi.WPAPIResponse import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequest @@ -102,7 +101,6 @@ abstract class BaseWPV2MediaRestClient( @Suppress("TooGenericExceptionCaught", "SwallowedException") private fun syncUploadMedia(site: SiteModel, media: MediaModel): Flow { fun ProducerScope.handleFailure(media: MediaModel, error: MediaError) { - media.setUploadState(FAILED) val payload = ProgressPayload(media, 1f, false, error) trySendBlocking(payload) close() diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt index 81f86eb99772..6c2178d1797b 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/media/MediaWPRESTResponse.kt @@ -5,7 +5,6 @@ package org.wordpress.android.fluxc.network.rest.wpapi.media import com.google.gson.annotations.SerializedName import org.apache.commons.text.StringEscapeUtils import org.wordpress.android.fluxc.model.MediaModel -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState import org.wordpress.android.fluxc.network.rest.JsonObjectOrNull import org.wordpress.android.util.DateTimeUtils import java.text.SimpleDateFormat @@ -53,10 +52,5 @@ fun MediaWPRESTResponse.toMediaModel(localSiteId: Int) = MediaModel( mimeType, StringEscapeUtils.unescapeHtml4(title.rendered), StringEscapeUtils.unescapeHtml4(caption.rendered), - StringEscapeUtils.unescapeHtml4(description.rendered), - if (status == "deleted") { - MediaUploadState.DELETED - } else { - MediaUploadState.UPLOADED - } + StringEscapeUtils.unescapeHtml4(description.rendered) ) diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index 09feb09c9235..e77058c686d2 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -148,8 +148,8 @@ class MediaLibraryCacheTest { "image/jpeg", // mimeType fileName, // title "", // caption - "", // description - MediaModel.MediaUploadState.UPLOADED // uploadState + "" // description + // uploadState ) } } diff --git a/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt index cf9b1e4af958..a4162ccc89d7 100644 --- a/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt +++ b/libs/fluxc/src/testFixtures/kotlin/org/wordpress/android/fluxc/media/MediaTestUtils.kt @@ -1,7 +1,6 @@ package org.wordpress.android.fluxc.media import org.wordpress.android.fluxc.model.MediaModel -import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState import org.wordpress.android.fluxc.utils.MimeTypes import java.util.concurrent.atomic.AtomicInteger @@ -28,7 +27,6 @@ object MediaTestUtils { private var filePath: String? = "/test/test-image.jpg" private var mimeType: String? = "image/jpeg" private var title: String? = "Test Image" - private var uploadState: MediaUploadState = MediaUploadState.UPLOADED fun localSiteId(value: Int) = apply { this.localSiteId = value } fun mediaId(value: Long) = apply { this.mediaId = value } @@ -38,7 +36,6 @@ object MediaTestUtils { fun filePath(value: String?) = apply { this.filePath = value } fun mimeType(value: String?) = apply { this.mimeType = value } fun title(value: String?) = apply { this.title = value } - fun uploadState(value: MediaUploadState) = apply { this.uploadState = value } fun build(): MediaModel { val media = MediaModel( @@ -47,8 +44,7 @@ object MediaTestUtils { this.fileName, this.filePath, this.mimeType, - this.title, - this.uploadState + this.title ).apply { setMediaId(this@LocalTestMediaBuilder.mediaId) setPostId(this@LocalTestMediaBuilder.postId) @@ -70,7 +66,6 @@ object MediaTestUtils { private var caption: String = "" private var description: String = "" private var alt: String = "" - private var uploadState: MediaUploadState = MediaUploadState.UPLOADED fun localSiteId(value: Int) = apply { this.localSiteId = value } fun mediaId(value: Long) = apply { this.mediaId = value } @@ -83,7 +78,6 @@ object MediaTestUtils { fun caption(value: String) = apply { this.caption = value } fun description(value: String) = apply { this.description = value } fun alt(value: String) = apply { this.alt = value } - fun uploadState(value: MediaUploadState) = apply { this.uploadState = value } fun build(): MediaModel { val media = MediaModel( @@ -97,7 +91,6 @@ object MediaTestUtils { this.title, this.caption, this.description, - this.uploadState ) media.id = nextId.getAndIncrement() return media From a7809f6d7149734d3cc5df1073d8e468f35d5c32 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Tue, 20 Jan 2026 16:29:45 +0100 Subject: [PATCH 14/27] Add WellSql migration to remove `MediaModel` table --- .../wordpress/android/fluxc/persistence/WellSqlConfig.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt index 9319a96aaa18..e8321cdec480 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt @@ -40,7 +40,7 @@ open class WellSqlConfig : DefaultWellConfig { annotation class AddOn override fun getDbVersion(): Int { - return 238 + return 239 } override fun getDbName(): String { @@ -2285,6 +2285,10 @@ open class WellSqlConfig : DefaultWellConfig { 238 -> migrate(version) { db.execSQL("DROP TABLE IF EXISTS MediaUploadModel") } + + 239 -> migrate(version) { + db.execSQL("DROP TABLE IF EXISTS MediaModel") + } } } db.setTransactionSuccessful() From 2bac569c535bc62469c0e0ca10f94d358c02ab67 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 21 Jan 2026 17:57:51 +0100 Subject: [PATCH 15/27] `MediaLibraryCacheTest` uses `MediaTestUtils#createRemoteTestMedia()` now Using a shared builder is both cleaner and safer. --- .../fluxc/store/MediaLibraryCacheTest.kt | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index e77058c686d2..cb0cf803468b 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -3,6 +3,7 @@ package org.wordpress.android.fluxc.store import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Test +import org.wordpress.android.fluxc.media.MediaTestUtils import org.wordpress.android.fluxc.model.MediaModel class MediaLibraryCacheTest { @@ -137,19 +138,6 @@ class MediaLibraryCacheTest { assertThat(result).isNull() } - private fun createTestMedia(id: Int, fileName: String): MediaModel { - return MediaModel( - 1, // localSiteId - id.toLong(), // mediaId - 0L, // postId - null, // uploadDate - "https://example.com/$fileName", // url - fileName, // fileName - "image/jpeg", // mimeType - fileName, // title - "", // caption - "" // description - // uploadState - ) - } + private fun createTestMedia(id: Int, fileName: String) = + MediaTestUtils.createRemoteTestMedia().mediaId(id.toLong()).fileName(fileName).build() } From 32d4ec084592a7a6c95d613e99da8711a6cfe3c2 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 21 Jan 2026 18:06:05 +0100 Subject: [PATCH 16/27] Remove `MediaUploadState` enum I simply forgot to do this in 521e2e44ec1c2412791574a9d0d108b2b814b1e8 --- .../wordpress/android/fluxc/model/MediaModel.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index 76d557edb52e..923645bdac86 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -10,21 +10,6 @@ import java.io.Serializable; public class MediaModel extends Payload implements Serializable { - public enum MediaUploadState { - QUEUED, UPLOADING, DELETING, DELETED, FAILED, UPLOADED; - - @NonNull - public static MediaUploadState fromString(@Nullable String stringState) { - if (stringState != null) { - for (MediaUploadState state : MediaUploadState.values()) { - if (stringState.equalsIgnoreCase(state.toString())) { - return state; - } - } - } - return UPLOADED; - } - } private int mId; From bc5c92311837df2e67c61292ba0945ac603a1b41 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 21 Jan 2026 18:10:18 +0100 Subject: [PATCH 17/27] Add `MediaModel#hashCode` To maintain the equals-hashCode contract --- .../java/org/wordpress/android/fluxc/model/MediaModel.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index 923645bdac86..aee7457a599d 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -8,6 +8,7 @@ import org.wordpress.android.util.StringUtils; import java.io.Serializable; +import java.util.Objects; public class MediaModel extends Payload implements Serializable { @@ -103,6 +104,11 @@ && getPostId() == otherMedia.getPostId() && StringUtils.equals(getCaption(), otherMedia.getCaption()); } + @Override + public int hashCode() { + return Objects.hash(mId, mLocalSiteId, mMediaId, mPostId, mUploadDate, mUrl, mFileName, mFilePath, mMimeType, mTitle, mCaption, mDescription); + } + public void setId(int id) { mId = id; } From 25bf86ff6541190dffc38d06c68cceed879b7ff0 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 21 Jan 2026 18:12:45 +0100 Subject: [PATCH 18/27] Adjust name of `MediaLibraryCacheTest` to reflect correct (remote) id --- .../org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index cb0cf803468b..ceaa16bff339 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -120,7 +120,7 @@ class MediaLibraryCacheTest { } @Test - fun `when removing media by local id, then it filters out media`() { + fun `when removing media by remote id, then it filters out media`() { val mediaToRemove = testMedia1 cache.cacheMediaList(1, listOf(mediaToRemove, testMedia2)) From fb896cbe4d84f7eb190ea165dbf04baf97d5154f Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 21 Jan 2026 18:27:57 +0100 Subject: [PATCH 19/27] Make `MediaStore#instantiateMediaModel` `@NonNull` --- .../com/woocommerce/android/media/FileUploadUtils.kt | 7 +------ .../java/org/wordpress/android/fluxc/store/MediaStore.java | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt index 7d37030f381f..cd8cabe25f90 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/media/FileUploadUtils.kt @@ -87,12 +87,7 @@ object FileUploadUtils { filenameWithExtension, ) val instantiatedMedia = mediaStore.instantiateMediaModel(media) - return if (instantiatedMedia != null) { - instantiatedMedia - } else { - WooLog.w(T.MEDIA, "We couldn't instantiate the media") - null - } + return instantiatedMedia } /** diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java index ba154becfc82..cec24994594e 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java @@ -481,7 +481,7 @@ public void onRegister() { // Getters // - @Nullable + @NonNull public MediaModel instantiateMediaModel(@NonNull MediaModel media) { media.setId(mMediaIdGenerator.generate(media.getFilePath()).getValue()); return media; From 307f316acc08efb6b3bf5d5d49eb3ec22576918b Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 21 Jan 2026 19:21:08 +0100 Subject: [PATCH 20/27] Add test for race condition in MediaLibraryCache.addOrUpdate() In this tests, we invoke `addOrUpdate` 10 times, on 10 different threads, at the same moment. Without making this method atomic, `MediaLibraryCache` won't record all 10 updates. This test should fail. --- .../fluxc/store/MediaLibraryCacheTest.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index ceaa16bff339..e46d7fc33684 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -5,6 +5,7 @@ import org.junit.Before import org.junit.Test import org.wordpress.android.fluxc.media.MediaTestUtils import org.wordpress.android.fluxc.model.MediaModel +import java.util.concurrent.CountDownLatch class MediaLibraryCacheTest { private lateinit var cache: MediaLibraryCache @@ -138,6 +139,32 @@ class MediaLibraryCacheTest { assertThat(result).isNull() } + @Test + fun `when multiple threads add media concurrently, then all updates are preserved`() { + val siteId = 1 + val threadCount = 10 + val latch = CountDownLatch(threadCount) + val startLatch = CountDownLatch(1) + + val mediaItems = (1..threadCount).map { createTestMedia(it, "image$it.jpg") } + + // Launch threads that all try to add media at the same time + mediaItems.map { media -> + Thread { + startLatch.await() + cache.addOrUpdate(siteId, media) + latch.countDown() + }.apply { start() } + } + + startLatch.countDown() + latch.await() + + assertThat(cache.getMediaList(siteId)) + .hasSize(threadCount) + .containsExactlyInAnyOrderElementsOf(mediaItems) + } + private fun createTestMedia(id: Int, fileName: String) = MediaTestUtils.createRemoteTestMedia().mediaId(id.toLong()).fileName(fileName).build() } From 8ba74459073db6a48f03f48166a894bec8f71f80 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Wed, 21 Jan 2026 19:22:01 +0100 Subject: [PATCH 21/27] Use `ConcurrentHashMap#compute` This asserts that accessing a specific `, List` is locked when one of the threads operates on it. --- .../android/fluxc/store/MediaLibraryCache.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt index b27b60f6900c..63b150d4fe87 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt @@ -18,20 +18,22 @@ class MediaLibraryCache @Inject constructor() { } fun addOrUpdate(localSiteId: Int, media: MediaModel) { - val currentList = cache[localSiteId] ?: emptyList() - val mutableList = currentList.toMutableList() - val existingIndex = mutableList.indexOfFirst { it.mediaId == media.mediaId } - if (existingIndex != -1) { - mutableList[existingIndex] = media - } else { - mutableList.add(media) + cache.compute(localSiteId) { _, currentList -> + val mutableList = (currentList ?: emptyList()).toMutableList() + val existingIndex = mutableList.indexOfFirst { it.mediaId == media.mediaId } + if (existingIndex != -1) { + mutableList[existingIndex] = media + } else { + mutableList.add(media) + } + mutableList } - cache[localSiteId] = mutableList } fun remove(localSiteId: Int, mediaId: Long) { - val currentList = cache[localSiteId] ?: return - cache[localSiteId] = currentList.filter { it.mediaId != mediaId } + cache.computeIfPresent(localSiteId) { _, currentList -> + currentList.filter { it.mediaId != mediaId } + } } fun clear() { From c92a68dbfc3898ac33b780efae7ddb8f13ae2856 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 23 Jan 2026 10:54:01 +0100 Subject: [PATCH 22/27] Apply visibility modifiers to `Media*` classes Also move tests from `fluxc-tests` module to the appropriate module (where SUT is declared) --- .../java/org/wordpress/android/fluxc/module/MediaModule.kt | 4 ++-- .../wordpress/android/fluxc/store/MediaCacheOperations.kt | 2 +- .../org/wordpress/android/fluxc/store/MediaIdGenerator.kt | 2 +- .../org/wordpress/android/fluxc/store/MediaLibraryCache.kt | 2 +- .../android/fluxc/store/TimestampMediaIdGenerator.kt | 2 +- .../org/wordpress/android/fluxc/store}/MediaStoreTest.java | 6 +----- .../android/fluxc/store/TimestampMediaIdGeneratorTest.kt | 2 +- 7 files changed, 8 insertions(+), 12 deletions(-) rename libs/{fluxc-tests/src/test/java/org/wordpress/android/fluxc/media => fluxc/src/test/java/org/wordpress/android/fluxc/store}/MediaStoreTest.java (97%) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt index 31767a6fd6c8..926d4d4c50c7 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/module/MediaModule.kt @@ -10,9 +10,9 @@ import kotlin.time.ExperimentalTime @OptIn(ExperimentalTime::class) @Module -interface MediaModule { +abstract class MediaModule { @Binds - fun bindMediaIdGenerator(generator: TimestampMediaIdGenerator): MediaIdGenerator + internal abstract fun bindMediaIdGenerator(generator: TimestampMediaIdGenerator): MediaIdGenerator companion object { @Provides diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt index 6aa181571f41..f2fda8db91ae 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt @@ -4,7 +4,7 @@ import org.wordpress.android.fluxc.model.MediaModel import java.util.Locale import javax.inject.Inject -class MediaCacheOperations @Inject constructor( +internal class MediaCacheOperations @Inject constructor( private val cache: MediaLibraryCache ) { fun getSiteImages(siteId: Int): List { diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt index 6b40b7f1d753..c25cd2e9ecd6 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaIdGenerator.kt @@ -2,6 +2,6 @@ package org.wordpress.android.fluxc.store import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId -interface MediaIdGenerator { +internal interface MediaIdGenerator { fun generate(filePath: String): LocalId } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt index 63b150d4fe87..83757c2d97a2 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt @@ -6,7 +6,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class MediaLibraryCache @Inject constructor() { +internal class MediaLibraryCache @Inject constructor() { private val cache = ConcurrentHashMap>() fun getMediaList(localSiteId: Int): List? { diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt index f15d433a55b2..7345d8a431d6 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TimestampMediaIdGenerator.kt @@ -6,7 +6,7 @@ import kotlin.time.Clock import kotlin.time.ExperimentalTime @OptIn(ExperimentalTime::class) -class TimestampMediaIdGenerator @Inject constructor(private val clock: Clock) : MediaIdGenerator { +internal class TimestampMediaIdGenerator @Inject constructor(private val clock: Clock) : MediaIdGenerator { override fun generate(filePath: String): LocalId { val combined = "$filePath:${clock.now().toEpochMilliseconds()}" return LocalId(combined.hashCode()) diff --git a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaStoreTest.java similarity index 97% rename from libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java rename to libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaStoreTest.java index 858a310d989b..d35c3d155d9e 100644 --- a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/media/MediaStoreTest.java +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaStoreTest.java @@ -1,4 +1,4 @@ -package org.wordpress.android.fluxc.media; +package org.wordpress.android.fluxc.store; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -17,10 +17,6 @@ import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.network.rest.wpapi.media.ApplicationPasswordsMediaRestClient; import org.wordpress.android.fluxc.network.rest.wpcom.media.wpv2.WPComV2MediaRestClient; -import org.wordpress.android.fluxc.store.MediaCacheOperations; -import org.wordpress.android.fluxc.store.MediaIdGenerator; -import org.wordpress.android.fluxc.store.MediaLibraryCache; -import org.wordpress.android.fluxc.store.MediaStore; import org.wordpress.android.fluxc.utils.MediaUtils; import java.util.List; diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt index d3597c23350d..ca269e65b76f 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/TimestampMediaIdGeneratorTest.kt @@ -8,7 +8,7 @@ import kotlin.time.ExperimentalTime import kotlin.time.Instant @OptIn(ExperimentalTime::class) -class TimestampMediaIdGeneratorTest { +internal class TimestampMediaIdGeneratorTest { lateinit var sut: TimestampMediaIdGenerator From 38dfa496b55dd6ae24c4b33b17591fce71acdead Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 23 Jan 2026 10:55:32 +0100 Subject: [PATCH 23/27] Make `MediaCacheOperations#getCacheSize` private --- .../wordpress/android/fluxc/store/MediaCacheOperations.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt index f2fda8db91ae..1fd3be210292 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt @@ -39,10 +39,6 @@ internal class MediaCacheOperations @Inject constructor( return searchByMimeTypeAndTerm(siteId, "application", searchTerm) } - fun getCacheSize(siteId: Int): Int { - return cache.getMediaList(siteId)?.size ?: 0 - } - fun getUploadedMediaCount(siteId: Int, mimeTypePrefix: String?): Int { if (mimeTypePrefix == null) { return getCacheSize(siteId) @@ -50,6 +46,10 @@ internal class MediaCacheOperations @Inject constructor( return filterByMimeType(siteId, mimeTypePrefix).size } + private fun getCacheSize(siteId: Int): Int { + return cache.getMediaList(siteId)?.size ?: 0 + } + private fun filterByMimeType(siteId: Int, mimeTypePrefix: String): List { val allMedia = cache.getMediaList(siteId) ?: return emptyList() return allMedia.filter { media -> From 6ebd22e26837c80370d016dfe6d79336ee458328 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 23 Jan 2026 10:59:22 +0100 Subject: [PATCH 24/27] Remove `MediaLibraryCache#clear` We actually don't ever call this method in production code. We don't have to, as it's only in-memory cache and identified via local site id (so when user changes sites, the old cache is not reused). --- .../android/fluxc/store/MediaLibraryCache.kt | 4 ---- .../android/fluxc/store/MediaLibraryCacheTest.kt | 11 ----------- 2 files changed, 15 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt index 83757c2d97a2..20b9e8108369 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt @@ -35,8 +35,4 @@ internal class MediaLibraryCache @Inject constructor() { currentList.filter { it.mediaId != mediaId } } } - - fun clear() { - cache.clear() - } } diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index e46d7fc33684..7315bb8d2af6 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -47,17 +47,6 @@ class MediaLibraryCacheTest { assertThat(cache.getMediaList(2)).isEqualTo(list2) } - @Test - fun `when clearing, then it removes all entries`() { - cache.cacheMediaList(1, listOf(testMedia1)) - cache.cacheMediaList(2, listOf(testMedia2)) - - cache.clear() - - assertThat(cache.getMediaList(1)).isNull() - assertThat(cache.getMediaList(2)).isNull() - } - @Test fun `when caching media list, then it overwrites previous value for same site`() { val list1 = listOf(testMedia1) From 3f95039484f327680b68931a622099326055ae69 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 23 Jan 2026 11:00:34 +0100 Subject: [PATCH 25/27] Change formatting of `MediaModel#hashCode` --- .../wordpress/android/fluxc/model/MediaModel.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java index aee7457a599d..923b5f21badf 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/MediaModel.java @@ -106,7 +106,20 @@ && getPostId() == otherMedia.getPostId() @Override public int hashCode() { - return Objects.hash(mId, mLocalSiteId, mMediaId, mPostId, mUploadDate, mUrl, mFileName, mFilePath, mMimeType, mTitle, mCaption, mDescription); + return Objects.hash( + mId, + mLocalSiteId, + mMediaId, + mPostId, + mUploadDate, + mUrl, + mFileName, + mFilePath, + mMimeType, + mTitle, + mCaption, + mDescription + ); } public void setId(int id) { From f60ab06f48c51509e0e47a3d85c1fa9db0cf093c Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 23 Jan 2026 11:01:43 +0100 Subject: [PATCH 26/27] Change formatting of `MediaLibraryCacheTest#createTestMedia` --- .../wordpress/android/fluxc/store/MediaLibraryCacheTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index 7315bb8d2af6..5b8b8db9a707 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -155,5 +155,8 @@ class MediaLibraryCacheTest { } private fun createTestMedia(id: Int, fileName: String) = - MediaTestUtils.createRemoteTestMedia().mediaId(id.toLong()).fileName(fileName).build() + MediaTestUtils.createRemoteTestMedia() + .mediaId(id.toLong()) + .fileName(fileName) + .build() } From f10272be7dd5cd8cd80e84fb82ee16ddc2550565 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Fri, 23 Jan 2026 11:06:26 +0100 Subject: [PATCH 27/27] Rename `MediaLibraryCache` -> `RemoteMediaCache` --- .../fluxc/store/MediaCacheOperations.kt | 2 +- .../android/fluxc/store/MediaStore.java | 18 ++++----- ...diaLibraryCache.kt => RemoteMediaCache.kt} | 2 +- .../fluxc/store/MediaLibraryCacheTest.kt | 4 +- .../android/fluxc/store/MediaStoreTest.java | 40 +++++++++---------- 5 files changed, 33 insertions(+), 33 deletions(-) rename libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/{MediaLibraryCache.kt => RemoteMediaCache.kt} (95%) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt index 1fd3be210292..2a1914e0fc86 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaCacheOperations.kt @@ -5,7 +5,7 @@ import java.util.Locale import javax.inject.Inject internal class MediaCacheOperations @Inject constructor( - private val cache: MediaLibraryCache + private val cache: RemoteMediaCache ) { fun getSiteImages(siteId: Int): List { return filterByMimeType(siteId, "image") diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java index cec24994594e..3383aa2701d7 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaStore.java @@ -406,7 +406,7 @@ public static MediaErrorType fromString(@Nullable String string) { private final WPComV2MediaRestClient mWPComV2MediaRestClient; private final ApplicationPasswordsMediaRestClient mApplicationPasswordsMediaRestClient; - @NonNull private final MediaLibraryCache mMediaLibraryCache; + @NonNull private final RemoteMediaCache mRemoteMediaCache; @NonNull private final MediaCacheOperations mMediaCacheOperations; @NonNull private final MediaIdGenerator mMediaIdGenerator; @@ -420,7 +420,7 @@ public static MediaErrorType fromString(@Nullable String string) { ApplicationPasswordsMediaRestClient applicationPasswordsMediaRestClient, ApplicationPasswordsConfiguration applicationPasswordsConfiguration, @NonNull FluxCCrashLogger crashLogger, - @NonNull MediaLibraryCache mediaLibraryCache, + @NonNull RemoteMediaCache remoteMediaCache, @NonNull MediaCacheOperations mediaCacheOperations, @NonNull MediaIdGenerator mediaIdGenerator) { super(dispatcher); @@ -428,7 +428,7 @@ public static MediaErrorType fromString(@Nullable String string) { mApplicationPasswordsMediaRestClient = applicationPasswordsMediaRestClient; mApplicationPasswordsConfiguration = applicationPasswordsConfiguration; mCrashLogger = crashLogger; - mMediaLibraryCache = mediaLibraryCache; + mRemoteMediaCache = remoteMediaCache; mMediaCacheOperations = mediaCacheOperations; mMediaIdGenerator = mediaIdGenerator; } @@ -545,7 +545,7 @@ void updateMedia(@Nullable MediaModel media, boolean emit) { if (media == null) { event.error = new MediaError(MediaErrorType.NULL_MEDIA_ARG); } else { - mMediaLibraryCache.addOrUpdate(media.getLocalSiteId(), media); + mRemoteMediaCache.addOrUpdate(media.getLocalSiteId(), media); event.mediaList.add(media); } @@ -626,7 +626,7 @@ private void performFetchMediaList(@NonNull FetchMediaListPayload payload) { private void performCancelUpload(@NonNull CancelMediaPayload payload) { MediaModel media = payload.media; if (payload.delete) { - mMediaLibraryCache.remove(payload.site.getId(), media.getMediaId()); + mRemoteMediaCache.remove(payload.site.getId(), media.getMediaId()); } if (payload.site.getOrigin() == SiteModel.ORIGIN_WPCOM_REST) { @@ -666,7 +666,7 @@ private void handleMediaCanceled(@NonNull ProgressPayload payload) { } private void updateFetchedMediaList(@NonNull FetchMediaListResponsePayload payload) { - List currentCache = mMediaLibraryCache.getMediaList(payload.site.getId()); + List currentCache = mRemoteMediaCache.getMediaList(payload.site.getId()); if (payload.loadedMore) { // Append to existing cache @@ -675,10 +675,10 @@ private void updateFetchedMediaList(@NonNull FetchMediaListResponsePayload paylo } List updatedList = new ArrayList<>(currentCache); updatedList.addAll(payload.mediaList); - mMediaLibraryCache.cacheMediaList(payload.site.getId(), updatedList); + mRemoteMediaCache.cacheMediaList(payload.site.getId(), updatedList); } else { // Replace entire cache with fresh data - mMediaLibraryCache.cacheMediaList(payload.site.getId(), new ArrayList<>(payload.mediaList)); + mRemoteMediaCache.cacheMediaList(payload.site.getId(), new ArrayList<>(payload.mediaList)); } } @@ -698,7 +698,7 @@ private void handleMediaListFetched(@NonNull FetchMediaListResponsePayload paylo private void handleMediaFetched(@NonNull MediaPayload payload) { OnMediaChanged onMediaChanged = new OnMediaChanged(MediaAction.FETCH_MEDIA, payload.error); if (payload.media != null) { - mMediaLibraryCache.addOrUpdate(payload.site.getId(), payload.media); + mRemoteMediaCache.addOrUpdate(payload.site.getId(), payload.media); onMediaChanged.mediaList = new ArrayList<>(); onMediaChanged.mediaList.add(payload.media); } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/RemoteMediaCache.kt similarity index 95% rename from libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt rename to libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/RemoteMediaCache.kt index 20b9e8108369..8ed6f8742fe5 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/MediaLibraryCache.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/RemoteMediaCache.kt @@ -6,7 +6,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -internal class MediaLibraryCache @Inject constructor() { +internal class RemoteMediaCache @Inject constructor() { private val cache = ConcurrentHashMap>() fun getMediaList(localSiteId: Int): List? { diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt index 5b8b8db9a707..1c91ef9070c6 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaLibraryCacheTest.kt @@ -8,13 +8,13 @@ import org.wordpress.android.fluxc.model.MediaModel import java.util.concurrent.CountDownLatch class MediaLibraryCacheTest { - private lateinit var cache: MediaLibraryCache + private lateinit var cache: RemoteMediaCache private lateinit var testMedia1: MediaModel private lateinit var testMedia2: MediaModel @Before fun setup() { - cache = MediaLibraryCache() + cache = RemoteMediaCache() testMedia1 = createTestMedia(1, "image1.jpg") testMedia2 = createTestMedia(2, "image2.jpg") } diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaStoreTest.java b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaStoreTest.java index d35c3d155d9e..14ab8502523d 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaStoreTest.java +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/MediaStoreTest.java @@ -23,8 +23,8 @@ @RunWith(RobolectricTestRunner.class) public class MediaStoreTest { - private final MediaLibraryCache mMediaLibraryCache = new MediaLibraryCache(); - private final MediaCacheOperations mMediaCacheOperations = new MediaCacheOperations(mMediaLibraryCache); + private final RemoteMediaCache mRemoteMediaCache = new RemoteMediaCache(); + private final MediaCacheOperations mMediaCacheOperations = new MediaCacheOperations(mRemoteMediaCache); private static class FakeMediaIdGenerator implements MediaIdGenerator { private int nextId = 1; @@ -41,7 +41,7 @@ private static class FakeMediaIdGenerator implements MediaIdGenerator { Mockito.mock(org.wordpress.android.fluxc.network.rest.wpapi.applicationpasswords .ApplicationPasswordsConfiguration.class), FakeCrashLogging.INSTANCE, - mMediaLibraryCache, + mRemoteMediaCache, mMediaCacheOperations, new FakeMediaIdGenerator() ); @@ -59,8 +59,8 @@ public void testGetSiteImages() { assertTrue(MediaUtils.isVideoMimeType(videoMedia.getMimeType())); MediaModel imageMedia = generateMediaFromPath(testSiteId, testImageId, testImagePath); assertTrue(MediaUtils.isImageMimeType(imageMedia.getMimeType())); - mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia); - mMediaLibraryCache.addOrUpdate(testSiteId, imageMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, videoMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, imageMedia); final List storeImages = mMediaStore.getSiteImages(getTestSiteWithLocalId(testSiteId)); assertNotNull(storeImages); @@ -94,9 +94,9 @@ public void testSearchSiteImages() { assertTrue(MediaUtils.isAudioMimeType(audioMedia.getMimeType())); // insert media of different types - mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia); - mMediaLibraryCache.addOrUpdate(testSiteId, imageMedia); - mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, videoMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, imageMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, audioMedia); // verify the correct media is returned final List storeImages = mMediaStore @@ -134,9 +134,9 @@ public void testSearchSiteVideos() { assertTrue(MediaUtils.isApplicationMimeType(documentMedia.getMimeType())); // insert media of different types - mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia1); - mMediaLibraryCache.addOrUpdate(testSiteId, videoMedia2); - mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, videoMedia1); + mRemoteMediaCache.addOrUpdate(testSiteId, videoMedia2); + mRemoteMediaCache.addOrUpdate(testSiteId, documentMedia); // verify the correct media is returned final List storeVideos = mMediaStore @@ -179,10 +179,10 @@ public void testSearchSiteAudio() { assertTrue(MediaUtils.isApplicationMimeType(documentMedia.getMimeType())); // insert media of different types - mMediaLibraryCache.addOrUpdate(testSiteId, imageMedia); - mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia1); - mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia2); - mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, imageMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, audioMedia1); + mRemoteMediaCache.addOrUpdate(testSiteId, audioMedia2); + mRemoteMediaCache.addOrUpdate(testSiteId, documentMedia); // verify the correct media is returned (just audio) final List storeAudio = mMediaStore @@ -236,11 +236,11 @@ public void testSearchSiteDocuments() { assertTrue(MediaUtils.isApplicationMimeType(documentMedia4.getMimeType())); // insert media of different types - mMediaLibraryCache.addOrUpdate(testSiteId, audioMedia); - mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia1); - mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia2); - mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia3); - mMediaLibraryCache.addOrUpdate(testSiteId, documentMedia4); + mRemoteMediaCache.addOrUpdate(testSiteId, audioMedia); + mRemoteMediaCache.addOrUpdate(testSiteId, documentMedia1); + mRemoteMediaCache.addOrUpdate(testSiteId, documentMedia2); + mRemoteMediaCache.addOrUpdate(testSiteId, documentMedia3); + mRemoteMediaCache.addOrUpdate(testSiteId, documentMedia4); // verify the correct media is returned (just documents) final List storeDocuments = mMediaStore