From 27b44f02e304f1799b04f91d54152874237434f7 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Fri, 4 Oct 2024 14:28:24 +0200 Subject: [PATCH 01/11] Make buttons configurable for AttachmentsPickerSystemTabFactory and fix the buttons getting cropped. Fix permission message shown below the buttons. --- .../compose/sample/ui/MessagesActivity.kt | 1 + ...AttachmentsPickerMediaCaptureTabFactory.kt | 37 +- .../AttachmentsPickerSystemTabFactory.kt | 424 ++++++++++-------- .../factory/AttachmentsPickerTabFactories.kt | 43 +- .../factory/MissingPermissionContent.kt | 9 +- .../attachments/factory/PickerMediaMode.kt | 22 + 6 files changed, 295 insertions(+), 241 deletions(-) create mode 100644 stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt diff --git a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt index 74ffe722d45..0455d22bed4 100644 --- a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt +++ b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt @@ -122,6 +122,7 @@ class MessagesActivity : BaseConnectedActivity() { val shapes = StreamShapes.defaultShapes() val messageComposerTheme = MessageComposerTheme.defaultTheme(isInDarkMode, typography, shapes, colors) ChatTheme( + useDefaultSystemMediaPicker = true, isInDarkMode = isInDarkMode, colors = colors, shapes = shapes, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt index 374be3c7690..9ff9f4d51ba 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt @@ -17,8 +17,6 @@ package io.getstream.chat.android.compose.ui.messages.attachments.factory import android.Manifest -import android.content.Context -import android.content.pm.PackageManager import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -39,6 +37,7 @@ import io.getstream.chat.android.compose.state.messages.attachments.MediaCapture import io.getstream.chat.android.compose.ui.theme.ChatTheme import io.getstream.chat.android.ui.common.contract.internal.CaptureMediaContract import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMetaData +import io.getstream.chat.android.ui.common.utils.isPermissionDeclared import java.io.File /** @@ -94,7 +93,7 @@ public class AttachmentsPickerMediaCaptureTabFactory(private val pickerMediaMode ) { val context = LocalContext.current - val requiresCameraPermission = isCameraPermissionDeclared(context) + val requiresCameraPermission = context.isPermissionDeclared(Manifest.permission.CAMERA) val cameraPermissionState = if (requiresCameraPermission) rememberPermissionState(permission = Manifest.permission.CAMERA) else null @@ -120,36 +119,4 @@ public class AttachmentsPickerMediaCaptureTabFactory(private val pickerMediaMode MissingPermissionContent(cameraPermissionState) } } - - /** - * Returns if we need to check for the camera permission or not. - * - * @param context The context of the app. - * @return If the camera permission is declared in the manifest or not. - */ - private fun isCameraPermissionDeclared(context: Context): Boolean { - return context.packageManager - .getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS) - .requestedPermissions - .contains(Manifest.permission.CAMERA) - } - - /** - * Define which media type will be allowed. - */ - public enum class PickerMediaMode { - PHOTO, - VIDEO, - PHOTO_AND_VIDEO, - } - - /** - * Map [PickerMediaMode] into [CaptureMediaContract.Mode] - */ - private val PickerMediaMode.mode: CaptureMediaContract.Mode - get() = when (this) { - PickerMediaMode.PHOTO -> CaptureMediaContract.Mode.PHOTO - PickerMediaMode.VIDEO -> CaptureMediaContract.Mode.VIDEO - PickerMediaMode.PHOTO_AND_VIDEO -> CaptureMediaContract.Mode.PHOTO_AND_VIDEO - } } 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 index 667d4cb7870..ba9ab826fd6 100644 --- 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 @@ -16,6 +16,7 @@ package io.getstream.chat.android.compose.ui.messages.attachments.factory +import android.Manifest import android.app.Activity import android.content.Intent import android.net.Uri @@ -27,7 +28,7 @@ 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.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -35,11 +36,15 @@ 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.layout.wrapContentSize +import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Card import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -56,23 +61,40 @@ 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 com.google.accompanist.permissions.PermissionState +import com.google.accompanist.permissions.PermissionStatus +import com.google.accompanist.permissions.rememberPermissionState 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.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.common.utils.isPermissionDeclared +import java.io.File /** * Holds the information required to add support for "files" tab in the attachment picker. + * + * @param filesAllowed If the option to pick files is included in the attachments picker. + * @param mediaAllowed If the option to pick media (images/videos) is included in the attachments picker. + * @param captureImageAllowed If the option to capture an image is included in the attachments picker. + * @param captureVideoAllowed If the option to capture a video is included in the attachments picker. + * @param pollAllowed If the option to create a poll is included in the attachments picker. */ -public class AttachmentsPickerSystemTabFactory(private val otherFactories: List) : - AttachmentsPickerTabFactory { +public class AttachmentsPickerSystemTabFactory( + private val filesAllowed: Boolean, + private val mediaAllowed: Boolean, + private val captureImageAllowed: Boolean, + private val captureVideoAllowed: Boolean, + private val pollAllowed: Boolean, +) : AttachmentsPickerTabFactory { + + private val pollFactory by lazy { AttachmentsPickerPollTabFactory() } /** * The attachment picker mode that this factory handles. @@ -118,200 +140,238 @@ public class AttachmentsPickerSystemTabFactory(private val otherFactories: List< onAttachmentsSubmitted: (List) -> Unit, ) { val context = LocalContext.current - val attachmentFilter = AttachmentFilter() val storageHelper: StorageHelperWrapper = remember { - StorageHelperWrapper(context, StorageHelper(), attachmentFilter) + 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 filePickerLauncher = rememberFilePickerLauncher { uri -> + onAttachmentsSubmitted(storageHelper.getAttachmentsMetadataFromUris(listOf(uri))) } - val imagePickerLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.PickVisualMedia(), - ) { uri: Uri? -> - // Handle the image URI - uri?.let { - val attachmentMetadata = storageHelper.getAttachmentsMetadataFromUris(listOf(uri)) - onAttachmentsSubmitted(attachmentMetadata) + val imagePickerLauncher = rememberImagePickerLauncher { uri -> + onAttachmentsSubmitted(storageHelper.getAttachmentsMetadataFromUris(listOf(uri))) + } + + val mediaPickerMode = resolveMediaPickerMode(captureImageAllowed, captureVideoAllowed) + var cameraPermissionDialogShown by remember { mutableStateOf(false) } + val cameraPermissionRequired = context.isPermissionDeclared(Manifest.permission.CAMERA) + val cameraPermissionState = + if (cameraPermissionRequired) rememberPermissionState(Manifest.permission.CAMERA) else null + + val captureLauncher = mediaPickerMode?.let { + rememberCaptureMediaLauncher(mediaPickerMode) { file -> + onAttachmentsSubmitted(listOf(AttachmentMetaData(context, file))) } } - 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, - ), - ) - }, - ), + var pollShown by remember { mutableStateOf(false) } + + val buttonsConfig = ButtonsConfig( + filesAllowed = filesAllowed, + mediaAllowed = mediaAllowed, + captureAllowed = mediaPickerMode != null, + pollAllowed = pollAllowed + ) + val buttonActions = ButtonActions( + onFilesClick = { filePickerLauncher.launch(filePickerIntent()) }, + onMediaClick = { + imagePickerLauncher + .launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)) + }, + onCaptureClick = { + // Permission grant is needed only if CAMERA is declared in the Manifest and is not yet granted + if (cameraPermissionRequired && cameraPermissionState?.status is PermissionStatus.Denied) { + cameraPermissionDialogShown = true + } else { + captureLauncher?.launch(Unit) + } + }, + onPollClick = { pollShown = true } ) - } -} -@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 } - } + ButtonRow(config = buttonsConfig, actions = buttonActions) - var pollSelected by remember { - mutableStateOf(false) - } - var mediaSelected by remember { - mutableStateOf(false) + if (pollShown) { + PollDialog( + factory = pollFactory, + attachments = attachments, + actions = PollDialogActions( + onAttachmentPickerAction = onAttachmentPickerAction, + onAttachmentsChanged = onAttachmentsChanged, + onAttachmentItemSelected = onAttachmentItemSelected, + onAttachmentsSubmitted = onAttachmentsSubmitted, + onDismissPollDialog = { pollShown = false } + ) + ) + } + + if (cameraPermissionDialogShown && cameraPermissionState != null) { + CameraPermissionDialog( + permissionState = cameraPermissionState, + onDismiss = { cameraPermissionDialogShown = false } + ) + } + + LaunchedEffect(cameraPermissionState?.status) { + cameraPermissionDialogShown = 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 }, - ), - ) + @Composable + private fun rememberFilePickerLauncher(onResult: (Uri) -> Unit) = + rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val uri = result.data?.data + uri?.let(onResult) + } + } - ButtonRow( - onFilesClick = actions.onFilesClick, - onImagesClick = actions.onImagesClick, - onMediaClick = { mediaSelected = !mediaSelected }, - onPollClick = { pollSelected = !pollSelected }, - ) + @Composable + private fun rememberImagePickerLauncher(onResult: (Uri) -> Unit) = + rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> + uri?.let(onResult) + } + + @Composable + private fun rememberCaptureMediaLauncher(mode: PickerMediaMode, onResult: (File) -> Unit) = + rememberLauncherForActivityResult(CaptureMediaContract(mode.mode)) { file -> + file?.let(onResult) + } + + private fun resolveMediaPickerMode(captureImageAllowed: Boolean, captureVideoAllowed: Boolean) = when { + captureImageAllowed && captureVideoAllowed -> PickerMediaMode.PHOTO_AND_VIDEO + captureImageAllowed -> PickerMediaMode.PHOTO + captureVideoAllowed -> PickerMediaMode.VIDEO + else -> null + } + + private fun filePickerIntent(): Intent { + val attachmentFilter = AttachmentFilter() + return 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) + } + } } @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, - ) +private fun ButtonRow( + config: ButtonsConfig, + actions: ButtonActions, +) { + LazyRow( + modifier = Modifier.fillMaxWidth(), + contentPadding = PaddingValues(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + if (config.filesAllowed) { + item { + FilesButton(actions.onFilesClick) + } + } + if (config.mediaAllowed) { + item { + MediaButton(actions.onMediaClick) + } + } + if (config.captureAllowed) { + item { + CaptureButton(actions.onCaptureClick) + } + } + if (config.pollAllowed) { + item { + PollButton(actions.onPollClick) + } + } } +} - if (params.pollSelected) { - Dialog( - properties = DialogProperties( - usePlatformDefaultWidth = false, - ), - onDismissRequest = actions.onDismissPollDialog, +@OptIn(ExperimentalPermissionsApi::class) +@Composable +private fun CameraPermissionDialog( + permissionState: PermissionState, + onDismiss: () -> Unit, +) { + Dialog(onDismissRequest = onDismiss) { + Box( + modifier = Modifier + .wrapContentSize() + .background(ChatTheme.colors.barsBackground, RoundedCornerShape(16.dp)), + contentAlignment = Alignment.BottomCenter ) { - 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, - ) - } + MissingPermissionContent(permissionState) } } } @Composable -private fun ButtonRow( - onFilesClick: () -> Unit, - onImagesClick: () -> Unit, - onMediaClick: () -> Unit, - onPollClick: () -> Unit, +private fun PollDialog( + factory: AttachmentsPickerPollTabFactory, + attachments: List, + actions: PollDialogActions, ) { - 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, + Dialog( + properties = DialogProperties( + usePlatformDefaultWidth = false, + ), + onDismissRequest = actions.onDismissPollDialog, ) { - buttons.forEach { button -> - button() + Box( + modifier = Modifier + .background(ChatTheme.colors.appBackground) + .fillMaxWidth() + .fillMaxHeight(), // Ensure the dialog fills the height + ) { + factory.PickerTabContent( + onAttachmentPickerAction = actions.onAttachmentPickerAction, + attachments = attachments, + onAttachmentsChanged = actions.onAttachmentsChanged, + onAttachmentItemSelected = actions.onAttachmentItemSelected, + onAttachmentsSubmitted = actions.onAttachmentsSubmitted, + ) } } } +@Composable +private fun FilesButton(onClick: () -> Unit) = + RoundedIconButton( + onClick = onClick, + 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), + ) + +@Composable +private fun MediaButton(onClick: () -> Unit) = + RoundedIconButton( + onClick = onClick, + 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), + ) + +@Composable +private fun CaptureButton(onClick: () -> Unit) = + RoundedIconButton( + onClick = onClick, + 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), + ) + +@Composable +private fun PollButton(onClick: () -> Unit) = + RoundedIconButton( + onClick = onClick, + 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), + ) + @Composable private fun RoundedIconButton( onClick: () -> Unit, @@ -363,32 +423,24 @@ private fun RoundedIconButton( // Data classes to combine parameters. -private data class InnerContentParams( - val otherFactories: List, - val attachments: List, +private data class ButtonsConfig( + val filesAllowed: Boolean, + val mediaAllowed: Boolean, + val captureAllowed: Boolean, + val pollAllowed: Boolean, ) -private data class InnerContentActions( - val onAttachmentPickerAction: (AttachmentPickerAction) -> Unit, - val onAttachmentsChanged: (List) -> Unit, - val onAttachmentItemSelected: (AttachmentPickerItemState) -> Unit, - val onAttachmentsSubmitted: (List) -> Unit, +private data class ButtonActions( val onFilesClick: () -> Unit, - val onImagesClick: () -> Unit, + val onMediaClick: () -> Unit, + val onCaptureClick: () -> Unit, + val onPollClick: () -> Unit, ) -private data class DialogContentActions( +private data class PollDialogActions( 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 145c5caacb8..f519ec4626e 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,15 +22,30 @@ 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, + /** + * Builds the default list of attachment picker tab factories (without requesting storage permission). + * + * @param filesAllowed If the option to pick files is included in the attachments picker. + * @param mediaAllowed If the option to pick media (images/videos) is included in the attachments picker. + * @param captureImageAllowed If the option to capture an image is included in the attachments picker. + * @param captureVideoAllowed If the option to capture a video is included in the attachments picker. + * @param pollAllowed If the option to create a poll is included in the attachments picker. + */ + public fun defaultFactoriesWithoutStoragePermissions( + filesAllowed: Boolean = true, + mediaAllowed: Boolean = true, + captureImageAllowed: Boolean = true, + captureVideoAllowed: Boolean = true, + pollAllowed: Boolean = true, + ): List { + val factory = AttachmentsPickerSystemTabFactory( + filesAllowed = filesAllowed, + mediaAllowed = mediaAllowed, + captureImageAllowed = captureImageAllowed, + captureVideoAllowed = captureVideoAllowed, + pollAllowed = pollAllowed ) - return listOf(AttachmentsPickerSystemTabFactory(otherFactories)) + return listOf(factory) } /** @@ -55,19 +70,13 @@ public object AttachmentsPickerTabFactories { if (filesTabEnabled) AttachmentsPickerFilesTabFactory() else null, when { takeImageEnabled && recordVideoEnabled -> - AttachmentsPickerMediaCaptureTabFactory( - AttachmentsPickerMediaCaptureTabFactory.PickerMediaMode.PHOTO_AND_VIDEO, - ) + AttachmentsPickerMediaCaptureTabFactory(PickerMediaMode.PHOTO_AND_VIDEO) takeImageEnabled -> - AttachmentsPickerMediaCaptureTabFactory( - AttachmentsPickerMediaCaptureTabFactory.PickerMediaMode.PHOTO, - ) + AttachmentsPickerMediaCaptureTabFactory(PickerMediaMode.PHOTO) recordVideoEnabled -> - AttachmentsPickerMediaCaptureTabFactory( - AttachmentsPickerMediaCaptureTabFactory.PickerMediaMode.VIDEO, - ) + AttachmentsPickerMediaCaptureTabFactory(PickerMediaMode.VIDEO) else -> null }, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt index 094abfeb591..05a4dfd98fd 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt @@ -19,7 +19,6 @@ package io.getstream.chat.android.compose.ui.messages.attachments.factory import android.Manifest import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.ButtonDefaults @@ -43,10 +42,14 @@ import io.getstream.chat.android.uiutils.util.openSystemSettings * The UI explains to the user which permission is missing and why we need it. * * @param permissionState A state object to control and observe permission status changes. + * @param modifier A [Modifier] for external customisation. */ @OptIn(ExperimentalPermissionsApi::class) @Composable -internal fun MissingPermissionContent(permissionState: PermissionState) { +internal fun MissingPermissionContent( + permissionState: PermissionState, + modifier: Modifier = Modifier +) { val title = when (permissionState.permission) { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_MEDIA_IMAGES, @@ -68,7 +71,7 @@ internal fun MissingPermissionContent(permissionState: PermissionState) { val context = LocalContext.current Column( - modifier = Modifier.fillMaxSize(), + modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally, ) { Text( diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt new file mode 100644 index 00000000000..fd6794cb8bb --- /dev/null +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt @@ -0,0 +1,22 @@ +package io.getstream.chat.android.compose.ui.messages.attachments.factory + +import io.getstream.chat.android.ui.common.contract.internal.CaptureMediaContract + +/** + * Defines which media type will be allowed. + */ +public enum class PickerMediaMode { + PHOTO, + VIDEO, + PHOTO_AND_VIDEO, +} + +/** + * Maps [PickerMediaMode] into [CaptureMediaContract.Mode]. + */ +internal val PickerMediaMode.mode: CaptureMediaContract.Mode + get() = when (this) { + PickerMediaMode.PHOTO -> CaptureMediaContract.Mode.PHOTO + PickerMediaMode.VIDEO -> CaptureMediaContract.Mode.VIDEO + PickerMediaMode.PHOTO_AND_VIDEO -> CaptureMediaContract.Mode.PHOTO_AND_VIDEO + } From 7d5b3f3a7d920742168c3671f4d7ba1d771de4a1 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Fri, 4 Oct 2024 14:32:31 +0200 Subject: [PATCH 02/11] Apply spotless. --- .../api/stream-chat-android-compose.api | 25 ++++++++++--------- .../AttachmentsPickerSystemTabFactory.kt | 12 ++++----- .../factory/AttachmentsPickerTabFactories.kt | 2 +- .../factory/MissingPermissionContent.kt | 2 +- .../attachments/factory/PickerMediaMode.kt | 16 ++++++++++++ 5 files changed, 37 insertions(+), 20 deletions(-) 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 f8c47783f35..91208462b30 100644 --- a/stream-chat-android-compose/api/stream-chat-android-compose.api +++ b/stream-chat-android-compose/api/stream-chat-android-compose.api @@ -1528,22 +1528,13 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory : io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactory { public static final field $stable I - public fun (Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode;)V + public fun (Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode;)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/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode : java/lang/Enum { - public static final field PHOTO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; - public static final field PHOTO_AND_VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; - public static final field VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; - public static fun values ()[Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; -} - public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactory : io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactory { public static final field $stable I public fun ()V @@ -1555,7 +1546,7 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac 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 (ZZZZZ)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; @@ -1567,7 +1558,8 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac 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 final fun defaultFactoriesWithoutStoragePermissions (ZZZZZ)Ljava/util/List; + public static synthetic fun defaultFactoriesWithoutStoragePermissions$default (Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories;ZZZZZILjava/lang/Object;)Ljava/util/List; } public abstract interface class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactory { @@ -1595,6 +1587,15 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac public final fun getLambda-1$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3; } +public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode : java/lang/Enum { + public static final field PHOTO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; + public static final field PHOTO_AND_VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; + public static final field VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; + public static fun values ()[Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; +} + public final class io/getstream/chat/android/compose/ui/messages/attachments/poll/ComposableSingletons$PollCreationDiscardDialogKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/messages/attachments/poll/ComposableSingletons$PollCreationDiscardDialogKt; public static field lambda-1 Lkotlin/jvm/functions/Function2; 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 index ba9ab826fd6..657741ccc11 100644 --- 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 @@ -170,7 +170,7 @@ public class AttachmentsPickerSystemTabFactory( filesAllowed = filesAllowed, mediaAllowed = mediaAllowed, captureAllowed = mediaPickerMode != null, - pollAllowed = pollAllowed + pollAllowed = pollAllowed, ) val buttonActions = ButtonActions( onFilesClick = { filePickerLauncher.launch(filePickerIntent()) }, @@ -186,7 +186,7 @@ public class AttachmentsPickerSystemTabFactory( captureLauncher?.launch(Unit) } }, - onPollClick = { pollShown = true } + onPollClick = { pollShown = true }, ) ButtonRow(config = buttonsConfig, actions = buttonActions) @@ -200,15 +200,15 @@ public class AttachmentsPickerSystemTabFactory( onAttachmentsChanged = onAttachmentsChanged, onAttachmentItemSelected = onAttachmentItemSelected, onAttachmentsSubmitted = onAttachmentsSubmitted, - onDismissPollDialog = { pollShown = false } - ) + onDismissPollDialog = { pollShown = false }, + ), ) } if (cameraPermissionDialogShown && cameraPermissionState != null) { CameraPermissionDialog( permissionState = cameraPermissionState, - onDismiss = { cameraPermissionDialogShown = false } + onDismiss = { cameraPermissionDialogShown = false }, ) } @@ -300,7 +300,7 @@ private fun CameraPermissionDialog( modifier = Modifier .wrapContentSize() .background(ChatTheme.colors.barsBackground, RoundedCornerShape(16.dp)), - contentAlignment = Alignment.BottomCenter + contentAlignment = Alignment.BottomCenter, ) { MissingPermissionContent(permissionState) } 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 f519ec4626e..18548f96e7e 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 @@ -43,7 +43,7 @@ public object AttachmentsPickerTabFactories { mediaAllowed = mediaAllowed, captureImageAllowed = captureImageAllowed, captureVideoAllowed = captureVideoAllowed, - pollAllowed = pollAllowed + pollAllowed = pollAllowed, ) return listOf(factory) } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt index 05a4dfd98fd..88429a123d6 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/MissingPermissionContent.kt @@ -48,7 +48,7 @@ import io.getstream.chat.android.uiutils.util.openSystemSettings @Composable internal fun MissingPermissionContent( permissionState: PermissionState, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val title = when (permissionState.permission) { Manifest.permission.READ_EXTERNAL_STORAGE, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt index fd6794cb8bb..a1433b9f668 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt @@ -1,3 +1,19 @@ +/* + * 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.compose.ui.messages.attachments.factory import io.getstream.chat.android.ui.common.contract.internal.CaptureMediaContract From 8006d01fcc6ba1ddd939735a85466dbd2785a649 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Fri, 4 Oct 2024 14:33:19 +0200 Subject: [PATCH 03/11] Revert change in sample app. --- .../getstream/chat/android/compose/sample/ui/MessagesActivity.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt index 0455d22bed4..74ffe722d45 100644 --- a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt +++ b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/MessagesActivity.kt @@ -122,7 +122,6 @@ class MessagesActivity : BaseConnectedActivity() { val shapes = StreamShapes.defaultShapes() val messageComposerTheme = MessageComposerTheme.defaultTheme(isInDarkMode, typography, shapes, colors) ChatTheme( - useDefaultSystemMediaPicker = true, isInDarkMode = isInDarkMode, colors = colors, shapes = shapes, From 14264e8b003374b70afbd0740ab4b6f9282288fd Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Fri, 4 Oct 2024 17:02:13 +0200 Subject: [PATCH 04/11] Fix camera permission check in XML system attachment picker and ensure all items are visible. --- .../AttachmentsPickerSystemFragment.kt | 20 +- ...m_ui_fragment_attachment_system_picker.xml | 243 +++++++++--------- 2 files changed, 144 insertions(+), 119 deletions(-) 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 index e397a202554..a61d40f9bb1 100644 --- 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 @@ -37,6 +37,7 @@ import io.getstream.chat.android.ui.feature.messages.composer.attachment.picker. 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.PermissionChecker import io.getstream.chat.android.ui.utils.extensions.getFragmentManager import io.getstream.chat.android.ui.utils.extensions.streamThemeInflater import java.io.File @@ -49,6 +50,8 @@ internal class AttachmentsPickerSystemFragment : Fragment() { private lateinit var style: AttachmentsPickerDialogStyle + private val permissionChecker: PermissionChecker = PermissionChecker() + /** * A listener invoked when attachments are selected in the attachment tab. */ @@ -114,7 +117,6 @@ internal class AttachmentsPickerSystemFragment : Fragment() { // 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()) @@ -146,7 +148,9 @@ internal class AttachmentsPickerSystemFragment : Fragment() { } captureMedia?.let { binding.buttonCapture.setOnClickListener { - captureMedia?.launch(Unit) + checkCameraPermissions { + captureMedia?.launch(Unit) + } } } @@ -184,6 +188,18 @@ internal class AttachmentsPickerSystemFragment : Fragment() { this.style = style } + private fun checkCameraPermissions(onPermissionGranted: () -> Unit) { + if (permissionChecker.isNeededToRequestForCameraPermissions(requireContext())) { + permissionChecker.checkCameraPermissions( + view = binding.root, + onPermissionDenied = { /* Do nothing */ }, + onPermissionGranted = onPermissionGranted + ) + } else { + onPermissionGranted() + } + } + companion object { fun newInstance( 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 index 7faae5b23e5..d8a819e71ee 100644 --- 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 @@ -21,169 +21,178 @@ android:layout_height="wrap_content" > - + app:layout_constraintTop_toTopOf="parent" + > - - + - + + + + + - + - + - - - - - - - + + + + + - + - + - - - - - - - + + + + + - + - + - - - - - - - + + + + + - - - + - + \ No newline at end of file From 0300c78c4ca524b2db0ec13f38cef7d021fdddfb Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Fri, 4 Oct 2024 17:08:04 +0200 Subject: [PATCH 05/11] Fix detekt findings. --- .../AttachmentsPickerSystemFragment.kt | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) 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 index a61d40f9bb1..c29443c29e2 100644 --- 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 @@ -132,20 +132,19 @@ internal class AttachmentsPickerSystemFragment : Fragment() { ), ) } - 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 = 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 { checkCameraPermissions { From b9ddb8f22d7e14c0516dc3c0658bd62d78da4386 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Fri, 4 Oct 2024 17:08:30 +0200 Subject: [PATCH 06/11] Apply spotless. --- .../factory/system/internal/AttachmentsPickerSystemFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index c29443c29e2..f0ceda2d80d 100644 --- 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 @@ -192,7 +192,7 @@ internal class AttachmentsPickerSystemFragment : Fragment() { permissionChecker.checkCameraPermissions( view = binding.root, onPermissionDenied = { /* Do nothing */ }, - onPermissionGranted = onPermissionGranted + onPermissionGranted = onPermissionGranted, ) } else { onPermissionGranted() From 57dafaca74abe837942b569822b8a6ceb5520649 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Fri, 4 Oct 2024 18:03:45 +0200 Subject: [PATCH 07/11] Update CHANGELOG.md. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a3fe34a3d3..7df6fb79a88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ ## stream-chat-android-ui-components ### 🐞 Fixed +- Fix `CAMERA` permission request when using the capture photo/video attachment picker from `AttachmentsPickerSystemTabFactory`. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) ### ⬆️ Improved @@ -69,10 +70,12 @@ ## stream-chat-android-compose ### 🐞 Fixed +- Fix `CAMERA` permission request when using the capture photo/video attachment picker from `AttachmentsPickerSystemTabFactory`. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) ### ⬆️ Improved ### ✅ Added +- Add configuration options to `AttachmentsPickerTabFactories#defaultFactoriesWithoutStoragePermissions` to customize which attachment pickers are allowed. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) ### ⚠️ Changed From b33f3c441937bb3990240325a76f05b67b90b1bc Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Mon, 7 Oct 2024 09:19:14 +0200 Subject: [PATCH 08/11] Suppress LongMethod for AttachmentsPickerSystemTabFactory#PickerTabContent. --- .../attachments/factory/AttachmentsPickerSystemTabFactory.kt | 1 + 1 file changed, 1 insertion(+) 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 index 657741ccc11..87806bd1728 100644 --- 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 @@ -131,6 +131,7 @@ public class AttachmentsPickerSystemTabFactory( * @param onAttachmentsSubmitted Handler to submit the selected attachments to the message composer. */ @OptIn(ExperimentalPermissionsApi::class) + @Suppress("LongMethod") @Composable override fun PickerTabContent( onAttachmentPickerAction: (AttachmentPickerAction) -> Unit, From 5953fbc9572a52b4e9562740d5a5f099d6483e68 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Mon, 7 Oct 2024 11:18:18 +0200 Subject: [PATCH 09/11] Improve permission checks for capture media (in compose). --- .../AttachmentsPickerSystemTabFactory.kt | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) 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 index 87806bd1728..82532c9dc8f 100644 --- 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 @@ -62,8 +62,9 @@ import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionState -import com.google.accompanist.permissions.PermissionStatus +import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState +import com.google.accompanist.permissions.shouldShowRationale 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 @@ -94,6 +95,10 @@ public class AttachmentsPickerSystemTabFactory( private val pollAllowed: Boolean, ) : AttachmentsPickerTabFactory { + private val mediaPickerContract = resolveMediaPickerMode(captureImageAllowed, captureVideoAllowed) + ?.mode + ?.let(::CaptureMediaContract) + private val pollFactory by lazy { AttachmentsPickerPollTabFactory() } /** @@ -153,16 +158,17 @@ public class AttachmentsPickerSystemTabFactory( onAttachmentsSubmitted(storageHelper.getAttachmentsMetadataFromUris(listOf(uri))) } - val mediaPickerMode = resolveMediaPickerMode(captureImageAllowed, captureVideoAllowed) - var cameraPermissionDialogShown by remember { mutableStateOf(false) } + val captureLauncher = rememberCaptureMediaLauncher { file -> + onAttachmentsSubmitted(listOf(AttachmentMetaData(context, file))) + } + var cameraRationaleShown by remember { mutableStateOf(false) } val cameraPermissionRequired = context.isPermissionDeclared(Manifest.permission.CAMERA) - val cameraPermissionState = - if (cameraPermissionRequired) rememberPermissionState(Manifest.permission.CAMERA) else null - - val captureLauncher = mediaPickerMode?.let { - rememberCaptureMediaLauncher(mediaPickerMode) { file -> - onAttachmentsSubmitted(listOf(AttachmentMetaData(context, file))) + val cameraPermissionState = if (cameraPermissionRequired) { + rememberPermissionState(Manifest.permission.CAMERA) { + if (it) captureLauncher?.launch(Unit) } + } else { + null } var pollShown by remember { mutableStateOf(false) } @@ -170,7 +176,7 @@ public class AttachmentsPickerSystemTabFactory( val buttonsConfig = ButtonsConfig( filesAllowed = filesAllowed, mediaAllowed = mediaAllowed, - captureAllowed = mediaPickerMode != null, + captureAllowed = mediaPickerContract != null, pollAllowed = pollAllowed, ) val buttonActions = ButtonActions( @@ -181,10 +187,12 @@ public class AttachmentsPickerSystemTabFactory( }, onCaptureClick = { // Permission grant is needed only if CAMERA is declared in the Manifest and is not yet granted - if (cameraPermissionRequired && cameraPermissionState?.status is PermissionStatus.Denied) { - cameraPermissionDialogShown = true - } else { + if (!cameraPermissionRequired || cameraPermissionState?.status?.isGranted == true) { captureLauncher?.launch(Unit) + } else if (cameraPermissionState?.status?.shouldShowRationale == true) { + cameraRationaleShown = true + } else { + cameraPermissionState?.launchPermissionRequest() } }, onPollClick = { pollShown = true }, @@ -206,15 +214,15 @@ public class AttachmentsPickerSystemTabFactory( ) } - if (cameraPermissionDialogShown && cameraPermissionState != null) { + if (cameraRationaleShown && cameraPermissionState != null) { CameraPermissionDialog( permissionState = cameraPermissionState, - onDismiss = { cameraPermissionDialogShown = false }, + onDismiss = { cameraRationaleShown = false }, ) } LaunchedEffect(cameraPermissionState?.status) { - cameraPermissionDialogShown = false + cameraRationaleShown = false } } @@ -234,9 +242,11 @@ public class AttachmentsPickerSystemTabFactory( } @Composable - private fun rememberCaptureMediaLauncher(mode: PickerMediaMode, onResult: (File) -> Unit) = - rememberLauncherForActivityResult(CaptureMediaContract(mode.mode)) { file -> - file?.let(onResult) + private fun rememberCaptureMediaLauncher(onResult: (File) -> Unit) = + mediaPickerContract?.let { + rememberLauncherForActivityResult(mediaPickerContract) { file -> + file?.let(onResult) + } } private fun resolveMediaPickerMode(captureImageAllowed: Boolean, captureVideoAllowed: Boolean) = when { From 20746a0e3ce1f61fcc8419ffd601ac1fcb669145 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Tue, 8 Oct 2024 09:17:11 +0200 Subject: [PATCH 10/11] AttachmentsPickerTabFactories backwards compatibility and deprecation. --- CHANGELOG.md | 5 +++- .../api/stream-chat-android-compose.api | 2 ++ .../AttachmentsPickerSystemTabFactory.kt | 24 +++++++++++++++++++ .../factory/AttachmentsPickerTabFactories.kt | 20 ++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d5e802bab..da479507bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### ⚠️ Changed - Deprecate `NotInFilterObject` because it is not supported backend-side anymore. [#5393](https://github.com/GetStream/stream-chat-android/pull/5393) +- Deprecate `AttachmentsPickerTabFactories.defaultFactoriesWithoutStoragePermissions()` in favor of a more configurable method. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) +- Deprecate `AttachmentsPickerSystemTabFactory(otherFactories)` in favor of a more configurable constructor. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) ### ❌ Removed - Remove `NotInFilterObject` because it is not supported backend-side anymore. [#5394](https://github.com/GetStream/stream-chat-android/pull/5394) @@ -78,7 +80,8 @@ - Added `ChannelListViewModel.refresh` method to refresh the channel list. [#5425](https://github.com/GetStream/stream-chat-android/pull/5425) - Add `PinnedMessageList` component for showing the list of pinned messages in a channel. [#5420](https://github.com/GetStream/stream-chat-android/pull/5420) - Add `formatMessageTitle` method to `MessagePreviewFormatter`, to allow message preview title formatting customization. [#5420](https://github.com/GetStream/stream-chat-android/pull/5420) -- Add configuration options to `AttachmentsPickerTabFactories#defaultFactoriesWithoutStoragePermissions` to customize which attachment pickers are allowed. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) +- Add `AttachmentsPickerTabFactories.defaultFactoriesWithoutStoragePermissions` to customize which attachment pickers are allowed. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) +- Add `AttachmentsPickerSystemTabFactory(filesAllowed, mediaAllowed, captureImageAllowed, captureVideoAllowed, pollAllowed)` to to customize which attachment pickers are allowed. [#5430](https://github.com/GetStream/stream-chat-android/pull/5430) ### ⚠️ Changed - Exposed `DefaultMessageComposerRecordingContent` and `DefaultAudioRecordButton` components. [#5433](https://github.com/GetStream/stream-chat-android/pull/5433) 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 de0a0752caf..0b96827c3b8 100644 --- a/stream-chat-android-compose/api/stream-chat-android-compose.api +++ b/stream-chat-android-compose/api/stream-chat-android-compose.api @@ -1546,6 +1546,7 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac 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 (ZZZZZ)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 @@ -1558,6 +1559,7 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac 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 final fun defaultFactoriesWithoutStoragePermissions (ZZZZZ)Ljava/util/List; public static synthetic fun defaultFactoriesWithoutStoragePermissions$default (Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactories;ZZZZZILjava/lang/Object;)Ljava/util/List; } 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 index 82532c9dc8f..a5e14ace5de 100644 --- 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 @@ -69,6 +69,8 @@ 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.contract.internal.CaptureMediaContract @@ -95,6 +97,28 @@ public class AttachmentsPickerSystemTabFactory( private val pollAllowed: Boolean, ) : AttachmentsPickerTabFactory { + /** + * Holds the information required to add support for "files" tab in the attachment picker. + * + * @param otherFactories A list of other [AttachmentsPickerTabFactory] used to handle different attachment pickers. + */ + @Deprecated( + message = "Use constructor(filesAllowed, mediaAllowed, captureImageAllowed, captureVideoAllowed, pollAllowed)" + + " instead.", + replaceWith = ReplaceWith( + expression = "AttachmentsPickerSystemTabFactory(filesAllowed, mediaAllowed, captureImageAllowed," + + " captureVideoAllowed, pollAllowed)", + ), + level = DeprecationLevel.WARNING, + ) + public constructor(otherFactories: List) : this( + filesAllowed = true, + mediaAllowed = true, + captureImageAllowed = otherFactories.any { it.attachmentsPickerMode == MediaCapture }, + captureVideoAllowed = otherFactories.any { it.attachmentsPickerMode == MediaCapture }, + pollAllowed = otherFactories.any { it.attachmentsPickerMode == Poll }, + ) + private val mediaPickerContract = resolveMediaPickerMode(captureImageAllowed, captureVideoAllowed) ?.mode ?.let(::CaptureMediaContract) 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 18548f96e7e..9a586b24584 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,26 @@ package io.getstream.chat.android.compose.ui.messages.attachments.factory */ public object AttachmentsPickerTabFactories { + @Deprecated( + message = "Use defaultFactoriesWithoutStoragePermissions(filesAllowed: Boolean, mediaAllowed: Boolean = true," + + " captureImageAllowed: Boolean, captureVideoAllowed: Boolean, pollAllowed: Boolean = true) instead.", + replaceWith = ReplaceWith( + expression = "defaultFactoriesWithoutStoragePermissions(filesAllowed, mediaAllowed, captureImageAllowed," + + " captureVideoAllowed, pollAllowed)", + ), + level = DeprecationLevel.WARNING, + ) + 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 (without requesting storage permission). * From f4da4a52c1b35c73b4f46a85f3e39367b2b08ec3 Mon Sep 17 00:00:00 2001 From: PetarVelikov Date: Tue, 8 Oct 2024 09:25:24 +0200 Subject: [PATCH 11/11] Revert PickerMediaMode extraction. --- .../api/stream-chat-android-compose.api | 20 +++++----- ...AttachmentsPickerMediaCaptureTabFactory.kt | 19 ++++++++++ .../AttachmentsPickerSystemTabFactory.kt | 7 ++-- .../factory/AttachmentsPickerTabFactories.kt | 12 ++++-- .../attachments/factory/PickerMediaMode.kt | 38 ------------------- 5 files changed, 41 insertions(+), 55 deletions(-) delete mode 100644 stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt 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 0b96827c3b8..aeb09d62e4c 100644 --- a/stream-chat-android-compose/api/stream-chat-android-compose.api +++ b/stream-chat-android-compose/api/stream-chat-android-compose.api @@ -1528,13 +1528,22 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory : io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactory { public static final field $stable I - public fun (Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode;)V + public fun (Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode;)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/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode : java/lang/Enum { + public static final field PHOTO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; + public static final field PHOTO_AND_VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; + public static final field VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; + public static fun values ()[Lio/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory$PickerMediaMode; +} + public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactory : io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerTabFactory { public static final field $stable I public fun ()V @@ -1589,15 +1598,6 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/fac public final fun getLambda-1$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3; } -public final class io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode : java/lang/Enum { - public static final field PHOTO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; - public static final field PHOTO_AND_VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; - public static final field VIDEO Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; - public static fun values ()[Lio/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode; -} - public final class io/getstream/chat/android/compose/ui/messages/attachments/poll/ComposableSingletons$PollCreationDiscardDialogKt { public static final field INSTANCE Lio/getstream/chat/android/compose/ui/messages/attachments/poll/ComposableSingletons$PollCreationDiscardDialogKt; public static field lambda-1 Lkotlin/jvm/functions/Function2; diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt index 9ff9f4d51ba..8afc3ae0c1a 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerMediaCaptureTabFactory.kt @@ -119,4 +119,23 @@ public class AttachmentsPickerMediaCaptureTabFactory(private val pickerMediaMode MissingPermissionContent(cameraPermissionState) } } + + /** + * Define which media type will be allowed. + */ + public enum class PickerMediaMode { + PHOTO, + VIDEO, + PHOTO_AND_VIDEO, + } + + /** + * Map [PickerMediaMode] into [CaptureMediaContract.Mode] + */ + private val PickerMediaMode.mode: CaptureMediaContract.Mode + get() = when (this) { + PickerMediaMode.PHOTO -> CaptureMediaContract.Mode.PHOTO + PickerMediaMode.VIDEO -> CaptureMediaContract.Mode.VIDEO + PickerMediaMode.PHOTO_AND_VIDEO -> CaptureMediaContract.Mode.PHOTO_AND_VIDEO + } } 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 index a5e14ace5de..7cd642c622f 100644 --- 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 @@ -120,7 +120,6 @@ public class AttachmentsPickerSystemTabFactory( ) private val mediaPickerContract = resolveMediaPickerMode(captureImageAllowed, captureVideoAllowed) - ?.mode ?.let(::CaptureMediaContract) private val pollFactory by lazy { AttachmentsPickerPollTabFactory() } @@ -274,9 +273,9 @@ public class AttachmentsPickerSystemTabFactory( } private fun resolveMediaPickerMode(captureImageAllowed: Boolean, captureVideoAllowed: Boolean) = when { - captureImageAllowed && captureVideoAllowed -> PickerMediaMode.PHOTO_AND_VIDEO - captureImageAllowed -> PickerMediaMode.PHOTO - captureVideoAllowed -> PickerMediaMode.VIDEO + captureImageAllowed && captureVideoAllowed -> CaptureMediaContract.Mode.PHOTO_AND_VIDEO + captureImageAllowed -> CaptureMediaContract.Mode.PHOTO + captureVideoAllowed -> CaptureMediaContract.Mode.VIDEO else -> null } 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 9a586b24584..ad56a4117d5 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 @@ -90,13 +90,19 @@ public object AttachmentsPickerTabFactories { if (filesTabEnabled) AttachmentsPickerFilesTabFactory() else null, when { takeImageEnabled && recordVideoEnabled -> - AttachmentsPickerMediaCaptureTabFactory(PickerMediaMode.PHOTO_AND_VIDEO) + AttachmentsPickerMediaCaptureTabFactory( + AttachmentsPickerMediaCaptureTabFactory.PickerMediaMode.PHOTO_AND_VIDEO, + ) takeImageEnabled -> - AttachmentsPickerMediaCaptureTabFactory(PickerMediaMode.PHOTO) + AttachmentsPickerMediaCaptureTabFactory( + AttachmentsPickerMediaCaptureTabFactory.PickerMediaMode.PHOTO, + ) recordVideoEnabled -> - AttachmentsPickerMediaCaptureTabFactory(PickerMediaMode.VIDEO) + AttachmentsPickerMediaCaptureTabFactory( + AttachmentsPickerMediaCaptureTabFactory.PickerMediaMode.VIDEO, + ) else -> null }, diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt deleted file mode 100644 index a1433b9f668..00000000000 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/PickerMediaMode.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.compose.ui.messages.attachments.factory - -import io.getstream.chat.android.ui.common.contract.internal.CaptureMediaContract - -/** - * Defines which media type will be allowed. - */ -public enum class PickerMediaMode { - PHOTO, - VIDEO, - PHOTO_AND_VIDEO, -} - -/** - * Maps [PickerMediaMode] into [CaptureMediaContract.Mode]. - */ -internal val PickerMediaMode.mode: CaptureMediaContract.Mode - get() = when (this) { - PickerMediaMode.PHOTO -> CaptureMediaContract.Mode.PHOTO - PickerMediaMode.VIDEO -> CaptureMediaContract.Mode.VIDEO - PickerMediaMode.PHOTO_AND_VIDEO -> CaptureMediaContract.Mode.PHOTO_AND_VIDEO - }