From cac9aa76a95ad071ce776aa7f0d537fac5836da5 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Fri, 9 Oct 2026 13:04:22 -0400 Subject: [PATCH 1/2] fix(chat): guard MediaStore.getPickImagesMaxLimit on older pickers rememberChatPhotoPicker called getPickImagesMaxLimit() during composition with no version check. The method exists only on API 33+, or on API 30-32 with SDK extension R 2+, so an Android 12 device without the MediaProvider update threw NoSuchMethodError every time a chat opened (Bugsnag 6ac91865991833fd09984195, Samsung SM-N976V). Only call it where the system picker provides it. Elsewhere androidx falls back to a picker with no cap of its own, and the result callback already trims the selection to the remaining slots. --- .../shared/chat/ui/media/ChatPhotoPicker.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt index ab8a45e73..024cfa891 100644 --- a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt +++ b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt @@ -1,6 +1,8 @@ package com.flipcash.shared.chat.ui.media import android.net.Uri +import android.os.Build +import android.os.ext.SdkExtensions import android.provider.MediaStore import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest @@ -30,7 +32,19 @@ fun rememberChatPhotoPicker( val single = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> if (uri != null) currentOnPicked(listOf(uri)) } - val max = remaining.coerceIn(2, maxOf(2, MediaStore.getPickImagesMaxLimit())) + // getPickImagesMaxLimit() only exists where the system picker does (API 33+, or SDK + // extension R 2+); elsewhere androidx falls back to a picker with no cap of its own. + val systemMax = remember { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && + SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 2) + ) { + MediaStore.getPickImagesMaxLimit() + } else { + Int.MAX_VALUE + } + } + val max = remaining.coerceIn(2, maxOf(2, systemMax)) val multiple = rememberLauncherForActivityResult( ActivityResultContracts.PickMultipleVisualMedia(max), ) { uris -> From 7f23f4f4326a4ffe65ee82875537fa351827dcd9 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Fri, 9 Oct 2026 13:07:10 -0400 Subject: [PATCH 2/2] fix(chat): cap the fallback photo picker at the open slots Int.MAX_VALUE never reached the picker, since the clamp came out to remaining anyway, but it read as no cap. Fall back to remaining directly. --- .../com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt index 024cfa891..09878fe09 100644 --- a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt +++ b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/media/ChatPhotoPicker.kt @@ -33,7 +33,7 @@ fun rememberChatPhotoPicker( if (uri != null) currentOnPicked(listOf(uri)) } // getPickImagesMaxLimit() only exists where the system picker does (API 33+, or SDK - // extension R 2+); elsewhere androidx falls back to a picker with no cap of its own. + // extension R 2+). Without it, the open slots are the only cap. val systemMax = remember { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && @@ -41,10 +41,10 @@ fun rememberChatPhotoPicker( ) { MediaStore.getPickImagesMaxLimit() } else { - Int.MAX_VALUE + null } } - val max = remaining.coerceIn(2, maxOf(2, systemMax)) + val max = maxOf(2, systemMax?.let { minOf(remaining, it) } ?: remaining) val multiple = rememberLauncherForActivityResult( ActivityResultContracts.PickMultipleVisualMedia(max), ) { uris ->