From d0331d0a657c922d297017a5ab78f50b2d57e738 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Sat, 7 Nov 2026 21:24:38 -0500 Subject: [PATCH] fix(blob): stop AndroidUploadImageCache.uploadMimeFor recursing into itself The override delegated to the imported top-level uploadMimeFor, but inside the class the unqualified call resolved to the member, so every image upload through ImageUploadPreparer.prepare overflowed the stack. That breaks profile photo, group picture, group cover and create-group photo uploads on Android since #1923. Import the top-level function under an alias, and cover the real Android cache with a Robolectric test. --- .../app/blob/AndroidUploadImageCacheTest.kt | 45 +++++++++++++++++++ .../app/blob/AndroidUploadImageCache.kt | 4 +- 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 apps/flipcash/shared/blob/src/androidHostTest/kotlin/com/flipcash/app/blob/AndroidUploadImageCacheTest.kt diff --git a/apps/flipcash/shared/blob/src/androidHostTest/kotlin/com/flipcash/app/blob/AndroidUploadImageCacheTest.kt b/apps/flipcash/shared/blob/src/androidHostTest/kotlin/com/flipcash/app/blob/AndroidUploadImageCacheTest.kt new file mode 100644 index 0000000000..8b9dddde78 --- /dev/null +++ b/apps/flipcash/shared/blob/src/androidHostTest/kotlin/com/flipcash/app/blob/AndroidUploadImageCacheTest.kt @@ -0,0 +1,45 @@ +package com.flipcash.app.blob + +import android.content.Context +import android.webkit.MimeTypeMap +import androidx.test.core.app.ApplicationProvider +import com.getcode.util.resources.AndroidContentReader +import org.junit.Before +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows.shadowOf +import kotlin.test.Test +import kotlin.test.assertEquals + +/** The real cache, so a member that shadows the top-level `uploadMimeFor` recurses here instead of in production. */ +@RunWith(RobolectricTestRunner::class) +class AndroidUploadImageCacheTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + private val cache = AndroidUploadImageCache(context, AndroidContentReader(context)) + + @Before + fun registerMimeTypes() { + shadowOf(MimeTypeMap.getSingleton()).apply { + addExtensionMimeTypeMapping("jpeg", "image/jpeg") + addExtensionMimeTypeMapping("jpg", "image/jpeg") + addExtensionMimeTypeMapping("png", "image/png") + addExtensionMimeTypeMapping("heic", "image/heic") + } + } + + @Test + fun `a JPEG source uploads as JPEG`() { + assertEquals("image/jpeg", cache.uploadMimeFor("image/jpeg")) + } + + @Test + fun `a non-JPEG source uploads as PNG`() { + assertEquals("image/png", cache.uploadMimeFor("image/heic")) + } + + @Test + fun `an unknown source uploads as PNG`() { + assertEquals("image/png", cache.uploadMimeFor(null)) + } +} diff --git a/apps/flipcash/shared/blob/src/androidMain/kotlin/com/flipcash/app/blob/AndroidUploadImageCache.kt b/apps/flipcash/shared/blob/src/androidMain/kotlin/com/flipcash/app/blob/AndroidUploadImageCache.kt index ba925baf3f..fbbbe98681 100644 --- a/apps/flipcash/shared/blob/src/androidMain/kotlin/com/flipcash/app/blob/AndroidUploadImageCache.kt +++ b/apps/flipcash/shared/blob/src/androidMain/kotlin/com/flipcash/app/blob/AndroidUploadImageCache.kt @@ -2,7 +2,7 @@ package com.flipcash.app.blob import android.content.Context import com.getcode.util.resources.ContentReader -import com.getcode.util.resources.uploadMimeFor +import com.getcode.util.resources.uploadMimeFor as contentUploadMimeFor import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject @@ -13,7 +13,7 @@ internal class AndroidUploadImageCache @Inject constructor( ) : UploadImageCache { override fun mimeType(photo: PickedPhoto): String? = contentReader.mimeType(photo.toUri()) - override fun uploadMimeFor(sourceMimeType: String?): String = uploadMimeFor(sourceMimeType) + override fun uploadMimeFor(sourceMimeType: String?): String = contentUploadMimeFor(sourceMimeType) override fun copyToCache(photo: PickedPhoto, fileName: String, maxSize: Int, mimeType: String?): PickedPhoto? = contentReader.copyToCache(photo.toUri(), fileName, maxSize, mimeType)