diff --git a/docusaurus/docs/Android/ui/guides/pick-files-without-permissions.mdx b/docusaurus/docs/Android/ui/guides/pick-files-without-permissions.mdx
new file mode 100644
index 00000000000..61464402326
--- /dev/null
+++ b/docusaurus/docs/Android/ui/guides/pick-files-without-permissions.mdx
@@ -0,0 +1,81 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# System Media Picker
+
+Sometimes its not desirable for an app to have the `READ_MEDIA_IMAGES` and `READ_MEDIA_VIDEO` permissions.
+In this guide, we will explain how to set up the new `useDefaultSystemMediaPicker` parameter in both `ChatTheme` and `ChatUI`, why it is important, and how to remove permissions from the `AndroidManifest.xml` using `tools:node="remove"`.
+
+### The importance of System Media Picker
+
+The `useDefaultSystemMediaPicker` parameter allows you to use the system's default media picker instead of the custom media picker provided by the library. This can be beneficial for several reasons:
+- **Consistency**: Provides a consistent user experience by using the familiar system media picker.
+- **Permissions**: Reduces the need for additional permissions, as the system media picker handles permissions internally.
+- **Simplicity**: Simplifies the implementation by leveraging the built-in functionality of the system media picker.
+
+### Setting Up System Media Picker
+
+#### In Compose UI
+
+To enable the system media picker in `ChatTheme`, set the `useDefaultSystemMediaPicker` parameter to `true` when initializing the theme.
+
+```kotlin
+ChatTheme(
+ useDefaultSystemMediaPicker = true
+) {
+ // Your composable content
+}
+```
+
+#### In XML-based UI
+
+There are two options for enabling usage of the system media picker in XML-based UI: `XML attributes` and `StyleTransformer`.
+
+Let's start with the `XML Attributes` option.
+You can enable the system media picker by setting the `streamUiMessageComposerAttachmentsPickerSystemPickerEnabled` attribute to `true` in the `MessageComposerView` XML layout.
+
+```xml
+
+```
+
+You can also enable the system media picker using the `StyleTransformer` as shown below.
+
+```kotlin
+// Set the property directly in the AttachmentsPickerDialogStyle
+TransformStyle.attachmentsPickerStyleTransformer = StyleTransformer { defaultStyle ->
+ defaultStyle.copy(
+ useDefaultSystemMediaPicker = true,
+ )
+}
+
+// Or set the property in the MessageComposerStyle
+TransformStyle.messageComposerStyleTransformer = StyleTransformer { defaultStyle ->
+ defaultStyle.copy(
+ attachmentsPickerDialogStyle = defaultStyle.attachmentsPickerDialogStyle.copy(
+ useDefaultSystemMediaPicker = true,
+ ),
+ )
+}
+```
+
+Please be advised that setting `useDefaultSystemMediaPicker` in `MessageComposerStyle` will override the value set in `AttachmentsPickerDialogStyle`.
+
+### Removing Permissions from your Project
+
+Let's remove the permissions from the `AndroidManifest.xml`.
+
+When using the system media picker, you can remove unnecessary permissions from your `AndroidManifest.xml` to streamline your app's permission requests.
+Use the `tools:node="remove"` attribute to remove permissions.
+
+```xml
+
+
+
+```
+
+By following these steps you can remove unnecessary permissions from your `AndroidManifest.xml`.
\ No newline at end of file
diff --git a/stream-chat-android-compose/api/stream-chat-android-compose.api b/stream-chat-android-compose/api/stream-chat-android-compose.api
index 7bebbf530fa..ca28f80af5d 100644
--- a/stream-chat-android-compose/api/stream-chat-android-compose.api
+++ b/stream-chat-android-compose/api/stream-chat-android-compose.api
@@ -1520,11 +1520,21 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac
public fun isPickerTabEnabled (Lio/getstream/chat/android/models/Channel;)Z
}
+public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerSystemTabFactory : io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactory {
+ public static final field $stable I
+ public fun (Ljava/util/List;)V
+ public fun PickerTabContent (Lkotlin/jvm/functions/Function1;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)V
+ public fun PickerTabIcon (ZZLandroidx/compose/runtime/Composer;I)V
+ public fun getAttachmentsPickerMode ()Lio/getstream/chat/android/compose/state/messages/attachments/AttachmentsPickerMode;
+ public fun isPickerTabEnabled (Lio/getstream/chat/android/models/Channel;)Z
+}
+
public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories {
public static final field $stable I
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories;
public final fun defaultFactories (ZZZZZ)Ljava/util/List;
public static synthetic fun defaultFactories$default (Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories;ZZZZZILjava/lang/Object;)Ljava/util/List;
+ public final fun defaultFactoriesWithoutStoragePermissions ()Ljava/util/List;
}
public abstract interface class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactory {
@@ -1891,12 +1901,13 @@ public final class io/getstream/chat/android/compose/ui/theme/ChatTheme {
public final fun getStreamMediaRecorder (Landroidx/compose/runtime/Composer;I)Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;
public final fun getTimeProvider (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/ui/common/helper/TimeProvider;
public final fun getTypography (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/theme/StreamTypography;
+ public final fun getUseDefaultSystemMediaPicker (Landroidx/compose/runtime/Composer;I)Z
public final fun getVideoThumbnailsEnabled (Landroidx/compose/runtime/Composer;I)Z
public final fun isComposerLinkPreviewEnabled (Landroidx/compose/runtime/Composer;I)Z
}
public final class io/getstream/chat/android/compose/ui/theme/ChatThemeKt {
- public static final fun ChatTheme (ZZZLio/getstream/chat/android/compose/ui/theme/StreamColors;Lio/getstream/chat/android/compose/ui/theme/StreamDimens;Lio/getstream/chat/android/compose/ui/theme/StreamTypography;Lio/getstream/chat/android/compose/ui/theme/StreamShapes;Landroidx/compose/material/ripple/RippleTheme;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lio/getstream/chat/android/compose/ui/util/ReactionIconFactory;Lio/getstream/chat/android/compose/ui/theme/ReactionOptionsTheme;Lio/getstream/chat/android/compose/ui/util/PollSwitchItemFactory;ZLio/getstream/chat/android/ui/common/helper/DateFormatter;Lio/getstream/chat/android/ui/common/helper/TimeProvider;Lio/getstream/chat/android/ui/common/utils/ChannelNameFormatter;Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;Lio/getstream/chat/android/compose/ui/util/SearchResultNameFormatter;Lio/getstream/chat/android/compose/ui/util/StreamCoilImageLoaderFactory;Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider;Lio/getstream/chat/android/compose/ui/theme/MessageOptionsTheme;Lio/getstream/chat/android/ui/common/state/messages/list/MessageOptionsUserReactionAlignment;Ljava/util/List;ZLio/getstream/chat/android/ui/common/images/resizing/StreamCdnImageResizing;ZLio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageUnreadSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme;Lio/getstream/chat/android/compose/ui/theme/AttachmentPickerTheme;Lio/getstream/chat/android/compose/ui/util/MessageTextFormatter;Lio/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;IIIIII)V
+ public static final fun ChatTheme (ZZZZLio/getstream/chat/android/compose/ui/theme/StreamColors;Lio/getstream/chat/android/compose/ui/theme/StreamDimens;Lio/getstream/chat/android/compose/ui/theme/StreamTypography;Lio/getstream/chat/android/compose/ui/theme/StreamShapes;Landroidx/compose/material/ripple/RippleTheme;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lio/getstream/chat/android/compose/ui/util/ReactionIconFactory;Lio/getstream/chat/android/compose/ui/theme/ReactionOptionsTheme;Lio/getstream/chat/android/compose/ui/util/PollSwitchItemFactory;ZLio/getstream/chat/android/ui/common/helper/DateFormatter;Lio/getstream/chat/android/ui/common/helper/TimeProvider;Lio/getstream/chat/android/ui/common/utils/ChannelNameFormatter;Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;Lio/getstream/chat/android/compose/ui/util/SearchResultNameFormatter;Lio/getstream/chat/android/compose/ui/util/StreamCoilImageLoaderFactory;Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider;Lio/getstream/chat/android/compose/ui/theme/MessageOptionsTheme;Lio/getstream/chat/android/ui/common/state/messages/list/MessageOptionsUserReactionAlignment;Ljava/util/List;ZLio/getstream/chat/android/ui/common/images/resizing/StreamCdnImageResizing;ZLio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageUnreadSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme;Lio/getstream/chat/android/compose/ui/theme/AttachmentPickerTheme;Lio/getstream/chat/android/compose/ui/util/MessageTextFormatter;Lio/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;IIIIII)V
}
public final class io/getstream/chat/android/compose/ui/theme/ComponentSize {
@@ -2205,8 +2216,8 @@ public final class io/getstream/chat/android/compose/ui/theme/StreamColors$Compa
public final class io/getstream/chat/android/compose/ui/theme/StreamDimens {
public static final field $stable I
public static final field Companion Lio/getstream/chat/android/compose/ui/theme/StreamDimens$Companion;
- public synthetic fun (FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFIILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFLkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFIILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1-D9Ej5fM ()F
public final fun component10-D9Ej5fM ()F
public final fun component11-D9Ej5fM ()F
@@ -2255,12 +2266,13 @@ public final class io/getstream/chat/android/compose/ui/theme/StreamDimens {
public final fun component50-D9Ej5fM ()F
public final fun component51-D9Ej5fM ()F
public final fun component52-D9Ej5fM ()F
+ public final fun component53-D9Ej5fM ()F
public final fun component6-D9Ej5fM ()F
public final fun component7-D9Ej5fM ()F
public final fun component8-D9Ej5fM ()F
public final fun component9-D9Ej5fM ()F
- public final fun copy-EfCTRKU (FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)Lio/getstream/chat/android/compose/ui/theme/StreamDimens;
- public static synthetic fun copy-EfCTRKU$default (Lio/getstream/chat/android/compose/ui/theme/StreamDimens;FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFIILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/theme/StreamDimens;
+ public final fun copy-m-3Q45M (FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)Lio/getstream/chat/android/compose/ui/theme/StreamDimens;
+ public static synthetic fun copy-m-3Q45M$default (Lio/getstream/chat/android/compose/ui/theme/StreamDimens;FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFIILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/theme/StreamDimens;
public fun equals (Ljava/lang/Object;)Z
public final fun getAttachmentsContentFileUploadWidth-D9Ej5fM ()F
public final fun getAttachmentsContentFileWidth-D9Ej5fM ()F
@@ -2278,6 +2290,7 @@ public final class io/getstream/chat/android/compose/ui/theme/StreamDimens {
public final fun getAttachmentsContentVideoMaxHeight-D9Ej5fM ()F
public final fun getAttachmentsContentVideoWidth-D9Ej5fM ()F
public final fun getAttachmentsPickerHeight-D9Ej5fM ()F
+ public final fun getAttachmentsSystemPickerHeight-D9Ej5fM ()F
public final fun getChannelAvatarSize-D9Ej5fM ()F
public final fun getChannelItemHorizontalPadding-D9Ej5fM ()F
public final fun getChannelItemVerticalPadding-D9Ej5fM ()F
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt
index ed8d1996f04..8863b5bc22b 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt
@@ -646,7 +646,11 @@ public fun BoxScope.AttachmentsPickerMenu(
var isFullScreenContent by rememberSaveable { mutableStateOf(false) }
val screenHeight = LocalConfiguration.current.screenHeightDp
val pickerHeight by animateDpAsState(
- targetValue = if (isFullScreenContent) screenHeight.dp else ChatTheme.dimens.attachmentsPickerHeight,
+ targetValue = when {
+ isFullScreenContent -> screenHeight.dp
+ ChatTheme.useDefaultSystemMediaPicker -> ChatTheme.dimens.attachmentsSystemPickerHeight
+ else -> ChatTheme.dimens.attachmentsPickerHeight
+ },
label = "full sized picker animation",
)
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerSystemTabFactory.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerSystemTabFactory.kt
new file mode 100644
index 00000000000..667d4cb7870
--- /dev/null
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerSystemTabFactory.kt
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2014-2022 Stream.io Inc. All rights reserved.
+ *
+ * Licensed under the Stream License;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.getstream.chat.android.compose.ui.messages.attachments.factory
+
+import android.app.Activity
+import android.content.Intent
+import android.net.Uri
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Card
+import androidx.compose.material.Icon
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
+import com.google.accompanist.permissions.ExperimentalPermissionsApi
+import io.getstream.chat.android.compose.R
+import io.getstream.chat.android.compose.state.messages.attachments.AttachmentPickerItemState
+import io.getstream.chat.android.compose.state.messages.attachments.AttachmentsPickerMode
+import io.getstream.chat.android.compose.state.messages.attachments.Files
+import io.getstream.chat.android.compose.state.messages.attachments.MediaCapture
+import io.getstream.chat.android.compose.state.messages.attachments.Poll
+import io.getstream.chat.android.compose.ui.theme.ChatTheme
+import io.getstream.chat.android.compose.ui.util.StorageHelperWrapper
+import io.getstream.chat.android.ui.common.helper.internal.AttachmentFilter
+import io.getstream.chat.android.ui.common.helper.internal.StorageHelper
+import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMetaData
+
+/**
+ * Holds the information required to add support for "files" tab in the attachment picker.
+ */
+public class AttachmentsPickerSystemTabFactory(private val otherFactories: List) :
+ AttachmentsPickerTabFactory {
+
+ /**
+ * The attachment picker mode that this factory handles.
+ */
+ override val attachmentsPickerMode: AttachmentsPickerMode
+ get() = Files
+
+ /**
+ * Emits a file icon for this tab.
+ *
+ * @param isEnabled If the tab is enabled.
+ * @param isSelected If the tab is selected.
+ */
+ @Composable
+ override fun PickerTabIcon(isEnabled: Boolean, isSelected: Boolean) {
+ Icon(
+ painter = painterResource(id = R.drawable.stream_compose_ic_attachments),
+ contentDescription = stringResource(id = R.string.stream_compose_attachments),
+ tint = when {
+ isSelected -> ChatTheme.colors.primaryAccent
+ isEnabled -> ChatTheme.colors.textLowEmphasis
+ else -> ChatTheme.colors.disabled
+ },
+ )
+ }
+
+ /**
+ * Emits content that allows users to pick files in this tab.
+ *
+ * @param onAttachmentPickerAction A lambda that will be invoked when an action is happened.
+ * @param attachments The list of attachments to display.
+ * @param onAttachmentsChanged Handler to set the loaded list of attachments to display.
+ * @param onAttachmentItemSelected Handler when the item selection state changes.
+ * @param onAttachmentsSubmitted Handler to submit the selected attachments to the message composer.
+ */
+ @OptIn(ExperimentalPermissionsApi::class)
+ @Composable
+ override fun PickerTabContent(
+ onAttachmentPickerAction: (AttachmentPickerAction) -> Unit,
+ attachments: List,
+ onAttachmentsChanged: (List) -> Unit,
+ onAttachmentItemSelected: (AttachmentPickerItemState) -> Unit,
+ onAttachmentsSubmitted: (List) -> Unit,
+ ) {
+ val context = LocalContext.current
+ val attachmentFilter = AttachmentFilter()
+ val storageHelper: StorageHelperWrapper = remember {
+ StorageHelperWrapper(context, StorageHelper(), attachmentFilter)
+ }
+
+ val filePickerLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.StartActivityForResult(),
+ ) { result ->
+ // Handle the file URI
+ if (result.resultCode == Activity.RESULT_OK) {
+ val uri = result.data?.data
+ uri?.let {
+ val attachmentMetadata = storageHelper.getAttachmentsMetadataFromUris(listOf(uri))
+ onAttachmentsSubmitted(attachmentMetadata)
+ }
+ }
+ }
+
+ val imagePickerLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.PickVisualMedia(),
+ ) { uri: Uri? ->
+ // Handle the image URI
+ uri?.let {
+ val attachmentMetadata = storageHelper.getAttachmentsMetadataFromUris(listOf(uri))
+ onAttachmentsSubmitted(attachmentMetadata)
+ }
+ }
+
+ InnerContent(
+ InnerContentParams(
+ attachments = attachments,
+ otherFactories = otherFactories,
+ ),
+ InnerContentActions(
+ onAttachmentItemSelected = onAttachmentItemSelected,
+ onAttachmentsChanged = onAttachmentsChanged,
+ onAttachmentsSubmitted = onAttachmentsSubmitted,
+ onAttachmentPickerAction = onAttachmentPickerAction,
+ onFilesClick = {
+ // Start file picker
+ val filePickerIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
+ type = "*/*" // General type to include multiple types
+ putExtra(Intent.EXTRA_MIME_TYPES, attachmentFilter.getSupportedMimeTypes().toTypedArray())
+ addCategory(Intent.CATEGORY_OPENABLE)
+ }
+
+ filePickerLauncher.launch(filePickerIntent)
+ },
+ onImagesClick = {
+ // Start photo picker
+ imagePickerLauncher.launch(
+ PickVisualMediaRequest(
+ ActivityResultContracts.PickVisualMedia.ImageAndVideo,
+ ),
+ )
+ },
+ ),
+ )
+ }
+}
+
+@Composable
+private fun InnerContent(params: InnerContentParams, actions: InnerContentActions) {
+ val pollsFactory = remember {
+ params.otherFactories.firstOrNull { it.attachmentsPickerMode == Poll }
+ }
+ val mediaCaptureTabFactory = remember {
+ params.otherFactories.firstOrNull { it.attachmentsPickerMode == MediaCapture }
+ }
+
+ var pollSelected by remember {
+ mutableStateOf(false)
+ }
+ var mediaSelected by remember {
+ mutableStateOf(false)
+ }
+
+ DialogContent(
+ DialogContentParams(
+ pollsFactory = pollsFactory,
+ mediaCaptureTabFactory = mediaCaptureTabFactory,
+ mediaSelected = mediaSelected,
+ pollSelected = pollSelected,
+ attachments = params.attachments,
+ ),
+ DialogContentActions(
+ onAttachmentPickerAction = actions.onAttachmentPickerAction,
+ onAttachmentsChanged = actions.onAttachmentsChanged,
+ onAttachmentItemSelected = actions.onAttachmentItemSelected,
+ onAttachmentsSubmitted = actions.onAttachmentsSubmitted,
+ onDismissPollDialog = { pollSelected = false },
+ ),
+ )
+
+ ButtonRow(
+ onFilesClick = actions.onFilesClick,
+ onImagesClick = actions.onImagesClick,
+ onMediaClick = { mediaSelected = !mediaSelected },
+ onPollClick = { pollSelected = !pollSelected },
+ )
+}
+
+@Composable
+private fun DialogContent(params: DialogContentParams, actions: DialogContentActions) {
+ if (params.mediaSelected) {
+ params.mediaCaptureTabFactory?.PickerTabContent(
+ onAttachmentPickerAction = actions.onAttachmentPickerAction,
+ attachments = params.attachments,
+ onAttachmentsChanged = actions.onAttachmentsChanged,
+ onAttachmentItemSelected = actions.onAttachmentItemSelected,
+ onAttachmentsSubmitted = actions.onAttachmentsSubmitted,
+ )
+ }
+
+ if (params.pollSelected) {
+ Dialog(
+ properties = DialogProperties(
+ usePlatformDefaultWidth = false,
+ ),
+ onDismissRequest = actions.onDismissPollDialog,
+ ) {
+ Box(
+ modifier = Modifier
+ .background(ChatTheme.colors.appBackground)
+ .fillMaxWidth()
+ .fillMaxHeight(), // Ensure the dialog fills the height
+ ) {
+ params.pollsFactory?.PickerTabContent(
+ onAttachmentPickerAction = actions.onAttachmentPickerAction,
+ attachments = params.attachments,
+ onAttachmentsChanged = actions.onAttachmentsChanged,
+ onAttachmentItemSelected = actions.onAttachmentItemSelected,
+ onAttachmentsSubmitted = actions.onAttachmentsSubmitted,
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun ButtonRow(
+ onFilesClick: () -> Unit,
+ onImagesClick: () -> Unit,
+ onMediaClick: () -> Unit,
+ onPollClick: () -> Unit,
+) {
+ val buttons = listOf<@Composable () -> Unit>(
+ {
+ RoundedIconButton(
+ onClick = onFilesClick,
+ iconPainter = painterResource(id = R.drawable.stream_compose_ic_file_picker),
+ contentDescription = stringResource(id = R.string.stream_compose_files_option),
+ text = stringResource(id = R.string.stream_compose_files_option),
+ )
+ },
+ {
+ RoundedIconButton(
+ onClick = onImagesClick,
+ iconPainter = painterResource(id = R.drawable.stream_compose_ic_image_picker),
+ contentDescription = stringResource(id = R.string.stream_compose_images_option),
+ text = stringResource(id = R.string.stream_compose_images_option),
+ )
+ },
+ {
+ RoundedIconButton(
+ onClick = onMediaClick,
+ iconPainter = painterResource(id = R.drawable.stream_compose_ic_media_picker),
+ contentDescription = stringResource(id = R.string.stream_ui_message_composer_capture_media_take_photo),
+ text = stringResource(id = R.string.stream_ui_message_composer_capture_media_take_photo),
+ )
+ },
+ {
+ RoundedIconButton(
+ onClick = onPollClick,
+ iconPainter = painterResource(id = R.drawable.stream_compose_ic_poll),
+ contentDescription = stringResource(id = R.string.stream_compose_poll_option),
+ text = stringResource(id = R.string.stream_compose_poll_option),
+ )
+ },
+ )
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ buttons.forEach { button ->
+ button()
+ }
+ }
+}
+
+@Composable
+private fun RoundedIconButton(
+ onClick: () -> Unit,
+ iconPainter: Painter,
+ contentDescription: String,
+ text: String,
+ iconTint: Color = ChatTheme.colors.overlayDark,
+) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier.padding(8.dp),
+ ) {
+ Card(
+ shape = CircleShape,
+ elevation = 4.dp,
+ backgroundColor = ChatTheme.colors.barsBackground,
+ modifier = Modifier
+ .clip(CircleShape)
+ .size(72.dp)
+ .padding(12.dp),
+ ) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .clip(CircleShape)
+ .fillMaxSize()
+ .clickable(onClick = onClick),
+ ) {
+ Icon(
+ painter = iconPainter,
+ contentDescription = contentDescription,
+ tint = iconTint,
+ modifier = Modifier
+ .size(48.dp)
+ .padding(12.dp),
+ )
+ }
+ }
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = text,
+ style = ChatTheme.typography.footnote,
+ color = ChatTheme.colors.textLowEmphasis,
+ modifier = Modifier.padding(top = 4.dp),
+ )
+ }
+}
+
+// Data classes to combine parameters.
+
+private data class InnerContentParams(
+ val otherFactories: List,
+ val attachments: List,
+)
+
+private data class InnerContentActions(
+ val onAttachmentPickerAction: (AttachmentPickerAction) -> Unit,
+ val onAttachmentsChanged: (List) -> Unit,
+ val onAttachmentItemSelected: (AttachmentPickerItemState) -> Unit,
+ val onAttachmentsSubmitted: (List) -> Unit,
+ val onFilesClick: () -> Unit,
+ val onImagesClick: () -> Unit,
+)
+
+private data class DialogContentActions(
+ val onAttachmentPickerAction: (AttachmentPickerAction) -> Unit,
+ val onAttachmentsChanged: (List) -> Unit,
+ val onAttachmentItemSelected: (AttachmentPickerItemState) -> Unit,
+ val onAttachmentsSubmitted: (List) -> Unit,
+ val onDismissPollDialog: () -> Unit,
+)
+
+private data class DialogContentParams(
+ val pollsFactory: AttachmentsPickerTabFactory?,
+ val mediaCaptureTabFactory: AttachmentsPickerTabFactory?,
+ val mediaSelected: Boolean,
+ val pollSelected: Boolean,
+ val attachments: List,
+)
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories.kt
index 767aca44b8a..145c5caacb8 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories.kt
@@ -22,6 +22,17 @@ package io.getstream.chat.android.compose.ui.messages.attachments.factory
*/
public object AttachmentsPickerTabFactories {
+ public fun defaultFactoriesWithoutStoragePermissions(): List {
+ val otherFactories = defaultFactories(
+ imagesTabEnabled = false,
+ filesTabEnabled = false,
+ takeImageEnabled = true,
+ recordVideoEnabled = true,
+ pollEnabled = true,
+ )
+ return listOf(AttachmentsPickerSystemTabFactory(otherFactories))
+ }
+
/**
* Builds the default list of attachment picker tab factories.
*
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt
index f06278bd038..0c8b2cbce90 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt
@@ -77,6 +77,9 @@ private val LocalShapes = compositionLocalOf {
private val LocalAttachmentFactories = compositionLocalOf> {
error("No attachment factories provided! Make sure to wrap all usages of Stream components in a ChatTheme.")
}
+private val LocalUseDefaultSystemMediaPicker = compositionLocalOf {
+ error("No attachment factories provided! Make sure to wrap all usages of Stream components in a ChatTheme.")
+}
private val LocalAttachmentPreviewHandlers = compositionLocalOf> {
error("No attachment preview handlers provided! Make sure to wrap all usages of Stream components in a ChatTheme.")
}
@@ -232,6 +235,7 @@ public fun ChatTheme(
isInDarkMode: Boolean = isSystemInDarkTheme(),
autoTranslationEnabled: Boolean = false,
isComposerLinkPreviewEnabled: Boolean = false,
+ useDefaultSystemMediaPicker: Boolean = false,
colors: StreamColors = if (isInDarkMode) StreamColors.defaultDarkColors() else StreamColors.defaultColors(),
dimens: StreamDimens = StreamDimens.defaultDimens(),
typography: StreamTypography = StreamTypography.defaultTypography(),
@@ -259,7 +263,12 @@ public fun ChatTheme(
messageAlignmentProvider: MessageAlignmentProvider = MessageAlignmentProvider.defaultMessageAlignmentProvider(),
messageOptionsTheme: MessageOptionsTheme = MessageOptionsTheme.defaultTheme(),
messageOptionsUserReactionAlignment: MessageOptionsUserReactionAlignment = MessageOptionsUserReactionAlignment.END,
- attachmentsPickerTabFactories: List = AttachmentsPickerTabFactories.defaultFactories(),
+ attachmentsPickerTabFactories: List =
+ if (useDefaultSystemMediaPicker) {
+ AttachmentsPickerTabFactories.defaultFactoriesWithoutStoragePermissions()
+ } else {
+ AttachmentsPickerTabFactories.defaultFactories()
+ },
videoThumbnailsEnabled: Boolean = true,
streamCdnImageResizing: StreamCdnImageResizing = StreamCdnImageResizing.defaultStreamCdnImageResizing(),
readCountEnabled: Boolean = true,
@@ -313,6 +322,7 @@ public fun ChatTheme(
LocalTypography provides typography,
LocalShapes provides shapes,
LocalRippleTheme provides rippleTheme,
+ LocalUseDefaultSystemMediaPicker provides useDefaultSystemMediaPicker,
LocalAttachmentFactories provides attachmentFactories,
LocalAttachmentPreviewHandlers provides attachmentPreviewHandlers,
LocalQuotedAttachmentFactories provides quotedAttachmentFactories,
@@ -393,6 +403,11 @@ public object ChatTheme {
@ReadOnlyComposable
get() = LocalShapes.current
+ public val useDefaultSystemMediaPicker: Boolean
+ @Composable
+ @ReadOnlyComposable
+ get() = LocalUseDefaultSystemMediaPicker.current
+
/**
* Retrieves the current list of [AttachmentFactory] at the call site's position in the hierarchy.
*/
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDimens.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDimens.kt
index 6eaf2c1d0ec..7b98369d93d 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDimens.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDimens.kt
@@ -131,6 +131,7 @@ public data class StreamDimens(
public val groupAvatarInitialsXOffset: Dp,
public val groupAvatarInitialsYOffset: Dp,
public val attachmentsPickerHeight: Dp,
+ public val attachmentsSystemPickerHeight: Dp,
public val attachmentsContentImageMaxHeight: Dp,
public val attachmentsContentGiphyMaxWidth: Dp = attachmentsContentGiphyWidth,
public val attachmentsContentGiphyMaxHeight: Dp = attachmentsContentGiphyHeight,
@@ -192,6 +193,7 @@ public data class StreamDimens(
groupAvatarInitialsXOffset = 1.5.dp,
groupAvatarInitialsYOffset = 2.5.dp,
attachmentsPickerHeight = 350.dp,
+ attachmentsSystemPickerHeight = 220.dp,
attachmentsContentImageMaxHeight = 600.dp,
attachmentsContentVideoMaxHeight = 400.dp,
attachmentsContentMediaGridSpacing = 2.dp,
diff --git a/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/AttachmentFilter.kt b/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/AttachmentFilter.kt
index 4c129a44a29..5b1d62f08f1 100644
--- a/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/AttachmentFilter.kt
+++ b/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/AttachmentFilter.kt
@@ -21,6 +21,7 @@ import io.getstream.chat.android.client.ChatClient
import io.getstream.chat.android.core.internal.InternalStreamChatApi
import io.getstream.chat.android.models.AttachmentType
import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMetaData
+import io.getstream.log.taggedLogger
/**
* A filter that is used to filter out attachments that will not be accepted by the backend.
@@ -34,6 +35,8 @@ import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMet
public class AttachmentFilter(
private val chatClient: ChatClient = ChatClient.instance(),
) {
+ private val logger by taggedLogger("AttachmentFilter")
+
/**
* Filters out attachments that can be uploaded to the backend according to files
* and images upload configurations.
@@ -67,6 +70,29 @@ public class AttachmentFilter(
}
}
+ /**
+ * Returns the list of supported MIME types by the server according to the upload config values.
+ */
+ public fun getSupportedMimeTypes(): List {
+ val default = listOf("*/*") // All files
+ val fileUploadConfig = chatClient.getAppSettings().app.fileUploadConfig
+ val imageUploadConfig = chatClient.getAppSettings().app.imageUploadConfig
+
+ // Allowed
+ val allowedFileMimeTypes = fileUploadConfig.allowedMimeTypes.toTypedArray()
+ val allowedImageMimeTypes = imageUploadConfig.allowedMimeTypes.toTypedArray()
+ // Blocked
+ val blockedFileMimeTypes = fileUploadConfig.blockedMimeTypes.toTypedArray()
+ val blockedImageMimeTypes = imageUploadConfig.blockedMimeTypes.toTypedArray()
+
+ // Combined
+ val allowed = allowedFileMimeTypes + allowedImageMimeTypes
+ val blocked = blockedFileMimeTypes + blockedImageMimeTypes
+ val result = allowed.filterNot { it in blocked }
+ logger.d { "Supported MIME types: $result" }
+ return result.ifEmpty { default }
+ }
+
/**
* Checks if the attachment is allowed to be uploaded to the server according
* to the upload config values.
diff --git a/stream-chat-android-ui-common/src/main/res/values/strings.xml b/stream-chat-android-ui-common/src/main/res/values/strings.xml
index ba755b851e0..6defa44e131 100644
--- a/stream-chat-android-ui-common/src/main/res/values/strings.xml
+++ b/stream-chat-android-ui-common/src/main/res/values/strings.xml
@@ -33,6 +33,12 @@
Enable permissions on App Settings
Settings
+
+ Files
+ Media
+ Capture
+ Poll
+
The load failed due to an unknown error.
The load failed due to the invalid url.
diff --git a/stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/helper/AttachmentFilterTest.kt b/stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/helper/AttachmentFilterTest.kt
index 9d55bde0d7f..6f5e37b5ea3 100644
--- a/stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/helper/AttachmentFilterTest.kt
+++ b/stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/helper/AttachmentFilterTest.kt
@@ -14,14 +14,18 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalStreamChatApi::class)
+
package io.getstream.chat.android.ui.common.helper
import io.getstream.chat.android.client.ChatClient
+import io.getstream.chat.android.core.ExperimentalStreamChatApi
import io.getstream.chat.android.models.FileUploadConfig
import io.getstream.chat.android.positiveRandomLong
import io.getstream.chat.android.ui.common.helper.internal.AttachmentFilter
import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMetaData
import org.amshove.kluent.`should be equal to`
+import org.junit.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
@@ -34,6 +38,7 @@ internal class AttachmentFilterTest {
private val chatClient: ChatClient = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS)
+ @OptIn(ExperimentalStreamChatApi::class)
@ParameterizedTest
@MethodSource("attachmentFilterArguments")
fun `Given file and image upload configs When filtering attachments Should return only valid attachments`(
@@ -48,6 +53,27 @@ internal class AttachmentFilterTest {
filteredAttachments.size `should be equal to` attachmentFilterTestData.expectedAttachmentCount
}
+ @Test
+ fun `Given upload configs When getting supported MIME types Should return correct MIME types`() {
+ val fileUploadConfig = fileUploadConfig(
+ allowedMimeTypes = listOf("application/pdf", "text/plain"),
+ blockedMimeTypes = listOf("application/zip"),
+ )
+ val imageUploadConfig = fileUploadConfig(
+ allowedMimeTypes = listOf("image/jpeg", "image/png"),
+ blockedMimeTypes = listOf("image/gif"),
+ )
+
+ whenever(chatClient.getAppSettings().app.fileUploadConfig) doReturn fileUploadConfig
+ whenever(chatClient.getAppSettings().app.imageUploadConfig) doReturn imageUploadConfig
+
+ val attachmentFilter = AttachmentFilter(chatClient)
+
+ val supportedMimeTypes = attachmentFilter.getSupportedMimeTypes()
+
+ supportedMimeTypes `should be equal to` listOf("application/pdf", "text/plain", "image/jpeg", "image/png")
+ }
+
internal data class AttachmentFilterTestData(
val fileUploadConfig: FileUploadConfig,
val imageUploadConfig: FileUploadConfig,
diff --git a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api
index b61a803307d..5f88a08f7c4 100644
--- a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api
+++ b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api
@@ -1299,45 +1299,47 @@ public abstract interface class io/getstream/chat/android/ui/feature/messages/co
}
public final class io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle : io/getstream/chat/android/ui/helper/ViewStyle {
- public fun (ILio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/content/res/ColorStateList;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;ZLio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/Integer;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IZLandroid/graphics/drawable/Drawable;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;)V
- public final fun component1 ()I
- public final fun component10 ()Lio/getstream/chat/android/ui/font/TextStyle;
- public final fun component11 ()Z
- public final fun component12 ()Landroid/graphics/drawable/Drawable;
- public final fun component13 ()Ljava/lang/Integer;
- public final fun component14 ()Ljava/lang/String;
- public final fun component15 ()Lio/getstream/chat/android/ui/font/TextStyle;
- public final fun component16 ()Z
- public final fun component17 ()Landroid/graphics/drawable/Drawable;
- public final fun component18 ()Ljava/lang/String;
- public final fun component19 ()Landroid/graphics/drawable/Drawable;
- public final fun component2 ()Lio/getstream/chat/android/ui/font/TextStyle;
- public final fun component20 ()Ljava/lang/String;
- public final fun component21 ()Lio/getstream/chat/android/ui/font/TextStyle;
- public final fun component22 ()Landroid/graphics/drawable/Drawable;
- public final fun component23 ()Ljava/lang/String;
- public final fun component24 ()Lio/getstream/chat/android/ui/font/TextStyle;
+ public fun (ZILio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/content/res/ColorStateList;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;ZLio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/Integer;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IZLandroid/graphics/drawable/Drawable;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;)V
+ public synthetic fun (ZILio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/content/res/ColorStateList;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;ZLio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/Integer;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IZLandroid/graphics/drawable/Drawable;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Z
+ public final fun component10 ()Z
+ public final fun component11 ()Lio/getstream/chat/android/ui/font/TextStyle;
+ public final fun component12 ()Z
+ public final fun component13 ()Landroid/graphics/drawable/Drawable;
+ public final fun component14 ()Ljava/lang/Integer;
+ public final fun component15 ()Ljava/lang/String;
+ public final fun component16 ()Lio/getstream/chat/android/ui/font/TextStyle;
+ public final fun component17 ()Z
+ public final fun component18 ()Landroid/graphics/drawable/Drawable;
+ public final fun component19 ()Ljava/lang/String;
+ public final fun component2 ()I
+ public final fun component20 ()Landroid/graphics/drawable/Drawable;
+ public final fun component21 ()Ljava/lang/String;
+ public final fun component22 ()Lio/getstream/chat/android/ui/font/TextStyle;
+ public final fun component23 ()Landroid/graphics/drawable/Drawable;
+ public final fun component24 ()Ljava/lang/String;
public final fun component25 ()Lio/getstream/chat/android/ui/font/TextStyle;
public final fun component26 ()Lio/getstream/chat/android/ui/font/TextStyle;
- public final fun component27 ()Landroid/graphics/drawable/Drawable;
+ public final fun component27 ()Lio/getstream/chat/android/ui/font/TextStyle;
public final fun component28 ()Landroid/graphics/drawable/Drawable;
- public final fun component29 ()I
- public final fun component3 ()Landroid/graphics/drawable/Drawable;
- public final fun component30 ()Z
- public final fun component31 ()Landroid/graphics/drawable/Drawable;
- public final fun component32 ()Z
- public final fun component33 ()Landroid/graphics/drawable/Drawable;
- public final fun component34 ()Ljava/lang/String;
- public final fun component35 ()Landroid/graphics/drawable/Drawable;
- public final fun component36 ()Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;
- public final fun component4 ()Landroid/content/res/ColorStateList;
- public final fun component5 ()Z
- public final fun component6 ()Landroid/graphics/drawable/Drawable;
- public final fun component7 ()Ljava/lang/String;
- public final fun component8 ()Landroid/graphics/drawable/Drawable;
- public final fun component9 ()Z
- public final fun copy (ILio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/content/res/ColorStateList;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;ZLio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/Integer;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IZLandroid/graphics/drawable/Drawable;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;)Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;
- public static synthetic fun copy$default (Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;ILio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/content/res/ColorStateList;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;ZLio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/Integer;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IZLandroid/graphics/drawable/Drawable;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;IILjava/lang/Object;)Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;
+ public final fun component29 ()Landroid/graphics/drawable/Drawable;
+ public final fun component3 ()Lio/getstream/chat/android/ui/font/TextStyle;
+ public final fun component30 ()I
+ public final fun component31 ()Z
+ public final fun component32 ()Landroid/graphics/drawable/Drawable;
+ public final fun component33 ()Z
+ public final fun component34 ()Landroid/graphics/drawable/Drawable;
+ public final fun component35 ()Ljava/lang/String;
+ public final fun component36 ()Landroid/graphics/drawable/Drawable;
+ public final fun component37 ()Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;
+ public final fun component4 ()Landroid/graphics/drawable/Drawable;
+ public final fun component5 ()Landroid/content/res/ColorStateList;
+ public final fun component6 ()Z
+ public final fun component7 ()Landroid/graphics/drawable/Drawable;
+ public final fun component8 ()Ljava/lang/String;
+ public final fun component9 ()Landroid/graphics/drawable/Drawable;
+ public final fun copy (ZILio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/content/res/ColorStateList;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;ZLio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/Integer;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IZLandroid/graphics/drawable/Drawable;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;)Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;
+ public static synthetic fun copy$default (Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;ZILio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/content/res/ColorStateList;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;ZLio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/Integer;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Ljava/lang/String;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IZLandroid/graphics/drawable/Drawable;ZLandroid/graphics/drawable/Drawable;Ljava/lang/String;Landroid/graphics/drawable/Drawable;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/PickerMediaMode;IILjava/lang/Object;)Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;
public fun equals (Ljava/lang/Object;)Z
public final fun getAllowAccessButtonTextStyle ()Lio/getstream/chat/android/ui/font/TextStyle;
public final fun getAllowAccessToCameraButtonText ()Ljava/lang/String;
@@ -1370,6 +1372,7 @@ public final class io/getstream/chat/android/ui/feature/messages/composer/attach
public final fun getRecentFilesText ()Ljava/lang/String;
public final fun getRecentFilesTextStyle ()Lio/getstream/chat/android/ui/font/TextStyle;
public final fun getSubmitAttachmentsButtonIconDrawable ()Landroid/graphics/drawable/Drawable;
+ public final fun getUseDefaultSystemMediaPicker ()Z
public final fun getVideoIconDrawable ()Landroid/graphics/drawable/Drawable;
public final fun getVideoIconDrawableTint ()Ljava/lang/Integer;
public final fun getVideoIconVisible ()Z
@@ -1392,6 +1395,7 @@ public final class io/getstream/chat/android/ui/feature/messages/composer/attach
public final class io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactories {
public static final field INSTANCE Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactories;
public final fun defaultFactories (ZZZZ)Ljava/util/List;
+ public final fun defaultFactoriesWithoutPermissions (ZZZZ)Ljava/util/List;
}
public abstract interface class io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactory {
@@ -1429,6 +1433,12 @@ public final class io/getstream/chat/android/ui/feature/messages/composer/attach
public fun createTabIcon (Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;)Landroid/graphics/drawable/Drawable;
}
+public final class io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/system/AttachmentsPickerSystemTabFactory : io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactory {
+ public fun (ZZZZ)V
+ public fun createTabFragment (Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabListener;)Landroidx/fragment/app/Fragment;
+ public fun createTabIcon (Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle;)Landroid/graphics/drawable/Drawable;
+}
+
public final class io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment : androidx/appcompat/app/AppCompatDialogFragment {
public static final field Companion Lio/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment$Companion;
public static final field TAG Ljava/lang/String;
diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/MessageComposerViewStyle.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/MessageComposerViewStyle.kt
index f653d52a32a..0f991f788df 100644
--- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/MessageComposerViewStyle.kt
+++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/MessageComposerViewStyle.kt
@@ -1092,7 +1092,10 @@ public data class MessageComposerViewStyle(
}
@Suppress("MaxLineLength", "LongMethod", "ComplexMethod")
- private fun createAttachmentPickerDialogStyle(context: Context, a: TypedArray): AttachmentsPickerDialogStyle {
+ private fun createAttachmentPickerDialogStyle(
+ context: Context,
+ a: TypedArray,
+ ): AttachmentsPickerDialogStyle {
val attachmentsPickerBackgroundColor = a.getColor(
R.styleable.MessageComposerView_streamUiMessageComposerAttachmentsPickerBackgroundColor,
context.getColorCompat(R.color.stream_ui_white_smoke),
@@ -1360,7 +1363,13 @@ public data class MessageComposerViewStyle(
PickerMediaMode.PHOTO_AND_VIDEO,
)
+ val useDefaultSystemMediaPicker = a.getBoolean(
+ R.styleable.MessageComposerView_streamUiMessageComposerAttachmentsPickerSystemPickerEnabled,
+ false,
+ )
+
return AttachmentsPickerDialogStyle(
+ useDefaultSystemMediaPicker = useDefaultSystemMediaPicker,
attachmentsPickerBackgroundColor = attachmentsPickerBackgroundColor,
allowAccessButtonTextStyle = allowAccessButtonTextStyle,
submitAttachmentsButtonIconDrawable = submitAttachmentsButtonIconDrawable,
diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt
index 16e4242d18b..ef45f4c7d42 100644
--- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt
+++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt
@@ -22,8 +22,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.CheckedTextView
import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.descendants
+import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import com.google.android.material.card.MaterialCardView
import io.getstream.chat.android.models.Attachment
import io.getstream.chat.android.models.PollConfig
import io.getstream.chat.android.ui.R
@@ -171,6 +174,21 @@ public class AttachmentsPickerDialogFragment : BottomSheetDialogFragment() {
attachmentsPickerTabListener = attachmentsPickerTabListener,
)
binding.attachmentPager.isUserInputEnabled = false
+ setupPagerContainerHeight()
+ }
+
+ private fun setupPagerContainerHeight() {
+ val pagerContainer = binding.root.findViewById(R.id.pagerContainer)
+ val viewPager = binding.root.findViewById(R.id.attachmentPager)
+
+ viewPager.adapter?.let { adapter ->
+ if (adapter.itemCount == 1) {
+ val layoutParams = pagerContainer.layoutParams
+ layoutParams.height = ConstraintLayout.LayoutParams.WRAP_CONTENT
+ pagerContainer.layoutParams = layoutParams
+ }
+ }
+ pagerContainer.requestLayout()
}
override fun onDestroyView() {
@@ -262,13 +280,23 @@ public class AttachmentsPickerDialogFragment : BottomSheetDialogFragment() {
*/
public fun newInstance(
style: AttachmentsPickerDialogStyle,
- attachmentsPickerTabFactories: List = AttachmentsPickerTabFactories
- .defaultFactories(
- mediaAttachmentsTabEnabled = style.mediaAttachmentsTabEnabled,
- fileAttachmentsTabEnabled = style.fileAttachmentsTabEnabled,
- cameraAttachmentsTabEnabled = style.cameraAttachmentsTabEnabled,
- pollAttachmentsTabEnabled = style.pollAttachmentsTabEnabled,
- ),
+ attachmentsPickerTabFactories: List =
+ if (style.useDefaultSystemMediaPicker) {
+ AttachmentsPickerTabFactories
+ .defaultFactoriesWithoutPermissions(
+ mediaAttachmentsTabEnabled = style.mediaAttachmentsTabEnabled,
+ fileAttachmentsTabEnabled = style.fileAttachmentsTabEnabled,
+ cameraAttachmentsTabEnabled = style.cameraAttachmentsTabEnabled,
+ pollAttachmentsTabEnabled = style.pollAttachmentsTabEnabled,
+ )
+ } else {
+ AttachmentsPickerTabFactories.defaultFactories(
+ mediaAttachmentsTabEnabled = style.mediaAttachmentsTabEnabled,
+ fileAttachmentsTabEnabled = style.fileAttachmentsTabEnabled,
+ cameraAttachmentsTabEnabled = style.cameraAttachmentsTabEnabled,
+ pollAttachmentsTabEnabled = style.pollAttachmentsTabEnabled,
+ )
+ },
): AttachmentsPickerDialogFragment {
return AttachmentsPickerDialogFragment().apply {
setStyle(style)
diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle.kt
index 18d81b87da1..620f4d5765a 100644
--- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle.kt
+++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle.kt
@@ -25,6 +25,7 @@ import io.getstream.chat.android.ui.helper.ViewStyle
/**
* Style for [AttachmentsPickerDialogFragment].
*
+ * @param useDefaultSystemMediaPicker If the system pickers should be used that does not require the `READ_MEDIA` permission.
* @param attachmentsPickerBackgroundColor The background color of the picker.
* @param allowAccessButtonTextStyle The text style used for all the buttons used to request required permissions.
* @param submitAttachmentsButtonIconDrawable The icon for the submit selected attachments button.
@@ -61,6 +62,7 @@ import io.getstream.chat.android.ui.helper.ViewStyle
* @param pickerMediaMode define which media type will be allowed.
*/
public data class AttachmentsPickerDialogStyle(
+ val useDefaultSystemMediaPicker: Boolean = false,
@ColorInt val attachmentsPickerBackgroundColor: Int,
val allowAccessButtonTextStyle: TextStyle,
// Dialog header section
diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactories.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactories.kt
index 0dd865429ca..f7b73f4282b 100644
--- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactories.kt
+++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/AttachmentsPickerTabFactories.kt
@@ -20,12 +20,29 @@ import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.
import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.file.AttachmentsPickerFileTabFactory
import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.media.AttachmentsPickerMediaTabFactory
import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.poll.AttachmentsPickerPollTabFactory
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.system.AttachmentsPickerSystemTabFactory
/**
* Provides the default list of tab factories for the attachment picker.
*/
public object AttachmentsPickerTabFactories {
+ public fun defaultFactoriesWithoutPermissions(
+ mediaAttachmentsTabEnabled: Boolean,
+ fileAttachmentsTabEnabled: Boolean,
+ cameraAttachmentsTabEnabled: Boolean,
+ pollAttachmentsTabEnabled: Boolean,
+ ): List {
+ return listOf(
+ AttachmentsPickerSystemTabFactory(
+ mediaAttachmentsTabEnabled,
+ fileAttachmentsTabEnabled,
+ cameraAttachmentsTabEnabled,
+ pollAttachmentsTabEnabled,
+ ),
+ )
+ }
+
/**
* Creates a list of factories for the tabs that will be displayed in the attachment picker.
*
diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/camera/internal/CameraAttachmentFragment.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/camera/internal/CameraAttachmentFragment.kt
index e43cf71472d..02dca235120 100644
--- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/camera/internal/CameraAttachmentFragment.kt
+++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/camera/internal/CameraAttachmentFragment.kt
@@ -145,7 +145,7 @@ internal class CameraAttachmentFragment : Fragment() {
_binding = null
}
- private object LauncherRequestsKeys {
+ internal object LauncherRequestsKeys {
const val CAPTURE_MEDIA = "capture_media_request_key"
}
@@ -165,7 +165,7 @@ internal class CameraAttachmentFragment : Fragment() {
/**
* Map [PickerMediaMode] into [CaptureMediaContract.Mode]
*/
- private val PickerMediaMode.mode: CaptureMediaContract.Mode
+ internal val PickerMediaMode.mode: CaptureMediaContract.Mode
get() = when (this) {
PickerMediaMode.PHOTO -> CaptureMediaContract.Mode.PHOTO
PickerMediaMode.VIDEO -> CaptureMediaContract.Mode.VIDEO
diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/system/AttachmentsPickerSystemTabFactory.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/system/AttachmentsPickerSystemTabFactory.kt
new file mode 100644
index 00000000000..912084e0877
--- /dev/null
+++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/system/AttachmentsPickerSystemTabFactory.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
+ *
+ * Licensed under the Stream License;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.system
+
+import android.graphics.drawable.Drawable
+import androidx.fragment.app.Fragment
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.AttachmentsPickerDialogStyle
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.AttachmentsPickerTabFactory
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.AttachmentsPickerTabListener
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.system.internal.AttachmentsPickerSystemConfig
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.system.internal.AttachmentsPickerSystemFragment
+
+/**
+ * An attachment factory that creates a tab with the few icons and uses system pickers instead.
+ */
+public class AttachmentsPickerSystemTabFactory(
+ private val mediaAttachmentsTabEnabled: Boolean,
+ private val fileAttachmentsTabEnabled: Boolean,
+ private val cameraAttachmentsTabEnabled: Boolean,
+ private val pollAttachmentsTabEnabled: Boolean,
+) : AttachmentsPickerTabFactory {
+
+ /**
+ * Create the tab icon.
+ * @param style The style of the dialog.
+ */
+ override fun createTabIcon(style: AttachmentsPickerDialogStyle): Drawable {
+ return style.submitAttachmentsButtonIconDrawable
+ }
+
+ /**
+ * Create the tab fragment.
+ * @param style The style of the dialog.
+ * @param attachmentsPickerTabListener The listener for the tab.
+ */
+ override fun createTabFragment(
+ style: AttachmentsPickerDialogStyle,
+ attachmentsPickerTabListener: AttachmentsPickerTabListener,
+ ): Fragment {
+ return AttachmentsPickerSystemFragment.newInstance(
+ style,
+ attachmentsPickerTabListener,
+ AttachmentsPickerSystemConfig(
+ mediaAttachmentsTabEnabled,
+ fileAttachmentsTabEnabled,
+ cameraAttachmentsTabEnabled,
+ pollAttachmentsTabEnabled,
+ ),
+ )
+ }
+}
diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/system/internal/AttachmentsPickerSystemFragment.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/system/internal/AttachmentsPickerSystemFragment.kt
new file mode 100644
index 00000000000..e397a202554
--- /dev/null
+++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/factory/system/internal/AttachmentsPickerSystemFragment.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
+ *
+ * Licensed under the Stream License;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.system.internal
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.fragment.app.Fragment
+import io.getstream.chat.android.models.PollConfig
+import io.getstream.chat.android.ui.common.contract.internal.CaptureMediaContract
+import io.getstream.chat.android.ui.common.helper.internal.AttachmentFilter
+import io.getstream.chat.android.ui.common.helper.internal.StorageHelper
+import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMetaData
+import io.getstream.chat.android.ui.databinding.StreamUiFragmentAttachmentSystemPickerBinding
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.AttachmentsPickerDialogStyle
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.AttachmentsPickerTabListener
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.camera.internal.CameraAttachmentFragment.Companion.mode
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.factory.camera.internal.CameraAttachmentFragment.LauncherRequestsKeys
+import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker.poll.CreatePollDialogFragment
+import io.getstream.chat.android.ui.utils.extensions.getFragmentManager
+import io.getstream.chat.android.ui.utils.extensions.streamThemeInflater
+import java.io.File
+
+internal class AttachmentsPickerSystemFragment : Fragment() {
+
+ private lateinit var config: AttachmentsPickerSystemConfig
+ private var _binding: StreamUiFragmentAttachmentSystemPickerBinding? = null
+ private val binding get() = _binding!!
+
+ private lateinit var style: AttachmentsPickerDialogStyle
+
+ /**
+ * A listener invoked when attachments are selected in the attachment tab.
+ */
+ private var attachmentsPickerTabListener: AttachmentsPickerTabListener? = null
+ private val storageHelper = StorageHelper()
+ private val attachmentFilter = AttachmentFilter()
+ private val filePickerLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ val uri = result.data?.data
+ if (uri != null) {
+ val attachmentMetaData = storageHelper.getAttachmentsFromUriList(requireContext(), listOf(uri))
+ attachmentsPickerTabListener?.onSelectedAttachmentsChanged(attachmentMetaData)
+ }
+ attachmentsPickerTabListener?.onSelectedAttachmentsSubmitted()
+ }
+
+ private val imagePickerLauncher =
+ registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri: Uri? ->
+ if (uri != null) {
+ val attachmentMetaData = storageHelper.getAttachmentsFromUriList(requireContext(), listOf(uri))
+ attachmentsPickerTabListener?.onSelectedAttachmentsChanged(attachmentMetaData)
+ }
+ attachmentsPickerTabListener?.onSelectedAttachmentsSubmitted()
+ }
+
+ private var captureMedia: ActivityResultLauncher? = null
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ _binding =
+ StreamUiFragmentAttachmentSystemPickerBinding.inflate(
+ requireContext().streamThemeInflater,
+ container,
+ false,
+ )
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupViews()
+ }
+
+ private fun setupViews() {
+ // Adjust visibility of the tabs based on the enabled flags
+ if (!config.fileAttachmentsTabEnabled) {
+ binding.buttonFiles.visibility = View.GONE
+ binding.textFiles.visibility = View.GONE
+ }
+
+ if (!config.mediaAttachmentsTabEnabled) {
+ binding.buttonMedia.visibility = View.GONE
+ binding.textMedia.visibility = View.GONE
+ }
+
+ if (!config.cameraAttachmentsTabEnabled) {
+ binding.buttonCapture.visibility = View.GONE
+ binding.textCapture.visibility = View.GONE
+ }
+ if (!config.pollAttachmentsTabEnabled) {
+ binding.buttonPolls.visibility = View.GONE
+ binding.textPoll.visibility = View.GONE
+ }
+
+ // Setup listeners and actions
+ binding.buttonFiles.setOnClickListener {
+ val supportedMimeTypes = attachmentFilter.getSupportedMimeTypes()
+ val filePickerIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
+ type = "*/*" // General type to include multiple types
+ putExtra(Intent.EXTRA_MIME_TYPES, attachmentFilter.getSupportedMimeTypes().toTypedArray())
+ addCategory(Intent.CATEGORY_OPENABLE)
+ }
+
+ filePickerLauncher.launch(filePickerIntent)
+ }
+ binding.buttonMedia.setOnClickListener {
+ imagePickerLauncher.launch(
+ PickVisualMediaRequest(
+ mediaType = ActivityResultContracts.PickVisualMedia.ImageAndVideo,
+ ),
+ )
+ }
+ captureMedia = activity?.activityResultRegistry
+ ?.register(
+ LauncherRequestsKeys.CAPTURE_MEDIA,
+ CaptureMediaContract(style.pickerMediaMode.mode),
+ ) { file: File? ->
+ val result: List = if (file == null) {
+ emptyList()
+ } else {
+ listOf(AttachmentMetaData(requireContext(), file))
+ }
+
+ attachmentsPickerTabListener?.onSelectedAttachmentsChanged(result)
+ attachmentsPickerTabListener?.onSelectedAttachmentsSubmitted()
+ }
+ captureMedia?.let {
+ binding.buttonCapture.setOnClickListener {
+ captureMedia?.launch(Unit)
+ }
+ }
+
+ binding.buttonPolls.setOnClickListener {
+ context.getFragmentManager()?.let {
+ CreatePollDialogFragment.newInstance(object : CreatePollDialogFragment.CreatePollDialogListener {
+ override fun onCreatePoll(pollConfig: PollConfig) {
+ attachmentsPickerTabListener?.onPollSubmitted(pollConfig)
+ }
+
+ override fun onDismiss() {
+ attachmentsPickerTabListener?.onPollSubmitted(null)
+ }
+ })
+ .show(it, CreatePollDialogFragment.TAG)
+ }
+ }
+ }
+
+ /**
+ * Sets the listener invoked when attachments are selected in the attachment tab.
+ *
+ * @param attachmentsPickerTabListener The listener invoked when attachments are selected in the tab.
+ */
+ fun setAttachmentsPickerTabListener(attachmentsPickerTabListener: AttachmentsPickerTabListener) {
+ this.attachmentsPickerTabListener = attachmentsPickerTabListener
+ }
+
+ /**
+ * Set the style.
+ *
+ * @param style Style for the dialog.
+ */
+ fun setStyle(style: AttachmentsPickerDialogStyle) {
+ this.style = style
+ }
+
+ companion object {
+
+ fun newInstance(
+ style: AttachmentsPickerDialogStyle,
+ attachmentsPickerTabListener: AttachmentsPickerTabListener,
+ config: AttachmentsPickerSystemConfig,
+ ): Fragment = AttachmentsPickerSystemFragment().apply {
+ this.style = style
+ this.attachmentsPickerTabListener = attachmentsPickerTabListener
+ this.config = config
+ }
+ }
+}
+
+internal data class AttachmentsPickerSystemConfig(
+ val mediaAttachmentsTabEnabled: Boolean,
+ val fileAttachmentsTabEnabled: Boolean,
+ val cameraAttachmentsTabEnabled: Boolean,
+ val pollAttachmentsTabEnabled: Boolean,
+)
diff --git a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_attachment_system_picker.xml b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_attachment_system_picker.xml
new file mode 100644
index 00000000000..7faae5b23e5
--- /dev/null
+++ b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_attachment_system_picker.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stream-chat-android-ui-components/src/main/res/values/attrs_message_composer_view.xml b/stream-chat-android-ui-components/src/main/res/values/attrs_message_composer_view.xml
index fcdafdb7854..253c738d00f 100644
--- a/stream-chat-android-ui-components/src/main/res/values/attrs_message_composer_view.xml
+++ b/stream-chat-android-ui-components/src/main/res/values/attrs_message_composer_view.xml
@@ -314,6 +314,7 @@
+