diff --git a/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt b/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt index b265dee74fe..54d08f993c7 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt @@ -26,6 +26,8 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape @@ -39,10 +41,12 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.wire.android.R +import com.wire.android.ui.common.spacers.VerticalSpace import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography @@ -51,11 +55,13 @@ import com.wire.android.ui.theme.wireTypography fun AttachmentButton( text: String = "", @DrawableRes icon: Int, + labelStyle: TextStyle, modifier: Modifier = Modifier, onClick: () -> Unit ) { Column( modifier = modifier + .height(dimensions().spacing100x) .padding(dimensions().spacing4x) .clip(RoundedCornerShape(size = MaterialTheme.wireDimensions.buttonSmallCornerSize)) .clickable { onClick() } @@ -78,19 +84,22 @@ fun AttachmentButton( colorFilter = ColorFilter.tint(MaterialTheme.wireColorScheme.onPrimaryButtonEnabled) ) } + VerticalSpace.x4() + Spacer(modifier = Modifier.weight(1F)) Text( text = text, maxLines = 2, textAlign = TextAlign.Center, overflow = TextOverflow.Ellipsis, - style = MaterialTheme.wireTypography.button03, + style = labelStyle, color = MaterialTheme.wireColorScheme.onBackground, ) + Spacer(modifier = Modifier.weight(1F)) } } @Preview(showBackground = true) @Composable fun PreviewAttachmentButton() { - AttachmentButton("Share Location", R.drawable.ic_location) { } + AttachmentButton("Share Location", R.drawable.ic_location, MaterialTheme.wireTypography.button03) { } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt index 08f12273ed5..a0cc294d185 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt @@ -22,24 +22,31 @@ import android.net.Uri import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.material.Surface +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import com.wire.android.R import com.wire.android.ui.common.AttachmentButton import com.wire.android.ui.common.dimensions import com.wire.android.ui.home.conversations.model.UriAsset +import com.wire.android.ui.theme.wireTypography import com.wire.android.util.debug.LocalFeatureVisibilityFlags import com.wire.android.util.permission.UseCameraAndWriteStorageRequestFlow import com.wire.android.util.permission.UseCameraRequestFlow @@ -49,6 +56,7 @@ import com.wire.android.util.permission.rememberCurrentLocationFlow import com.wire.android.util.permission.rememberOpenFileBrowserFlow import com.wire.android.util.permission.rememberOpenGalleryFlow import com.wire.android.util.permission.rememberTakePictureFlow +import com.wire.android.util.ui.KeyboardHeight @Composable fun AttachmentOptionsComponent( @@ -58,6 +66,9 @@ fun AttachmentOptionsComponent( tempWritableVideoUri: Uri?, isFileSharingEnabled: Boolean ) { + val density = LocalDensity.current + val textMeasurer = rememberTextMeasurer() + val attachmentOptions = buildAttachmentOptionItems( isFileSharingEnabled, tempWritableImageUri, @@ -66,10 +77,31 @@ fun AttachmentOptionsComponent( onRecordAudioMessageClicked ) + val labelStyle = MaterialTheme.wireTypography.button03 + + /** + * Calculate the maximum text width among a list of attachment options. + */ + val maxTextWidth: Int = attachmentOptions + .map { optionItem -> + val label = stringResource(optionItem.text) + val longestLabel = if (label.contains(" ")) { + label.split(" ").maxBy { it.length } + } else { + label + } + // Measure the width of the longest label using the specified typography style + textMeasurer.measure( + longestLabel, + labelStyle + ).size.width + } + .maxBy { it } + BoxWithConstraints(Modifier.fillMaxSize()) { - val fullWidth: Dp = with(LocalDensity.current) { constraints.maxWidth.toDp() } - val minColumnWidth: Dp = dimensions().spacing80x - val minPadding: Dp = dimensions().spacing8x + val fullWidth: Dp = with(density) { constraints.maxWidth.toDp() } + val minPadding: Dp = dimensions().spacing2x + val minColumnWidth: Dp = with(density) { maxTextWidth.toDp() + dimensions().spacing24x } val visibleAttachmentOptions = attachmentOptions.filter { it.shouldShow } val params by remember(fullWidth, visibleAttachmentOptions.size) { derivedStateOf { @@ -87,20 +119,31 @@ fun AttachmentOptionsComponent( ) { visibleAttachmentOptions.forEach { option -> if (option.shouldShow) { - item { AttachmentButton(stringResource(option.text), option.icon) { option.onClick() } } + item { AttachmentButton(stringResource(option.text), option.icon, labelStyle) { option.onClick() } } } } } } } -private fun calculateGridParams(minPadding: Dp, minColumnWidth: Dp, fullWidth: Dp, itemsCount: Int): Pair { +private fun calculateGridParams( + minPadding: Dp, + minColumnWidth: Dp, + fullWidth: Dp, + itemsCount: Int +): Pair { + // Calculate the width available for columns by subtracting the minimum padding from both sides val availableWidth = fullWidth - (minPadding * 2) + // Determine the maximum number of columns that can fit in the available width val currentMaxColumns = availableWidth / minColumnWidth + // Check if the maximum number of columns is less than or equal to the number of items return if (currentMaxColumns <= itemsCount) { + // If so, use adaptive grid cells with the minimum column width and minimum padding GridCells.Adaptive(minColumnWidth) to PaddingValues(minPadding) } else { + // Otherwise, calculate the padding needed to center the columns val currentPadding = (availableWidth - (minColumnWidth * itemsCount)) / 2 + // Use fixed grid cells with the exact number of items and calculated padding GridCells.Fixed(itemsCount) to PaddingValues(vertical = minPadding, horizontal = currentPadding) } } @@ -241,7 +284,7 @@ private data class AttachmentOptionItem( val onClick: () -> Unit ) -@Preview(showBackground = true) +@Preview(showBackground = true, locale = "de") @Composable fun PreviewAttachmentComponents() { AttachmentOptionsComponent( @@ -252,3 +295,60 @@ fun PreviewAttachmentComponents() { onRecordAudioMessageClicked = {}, ) } + +@Preview(name = "Small Screen", widthDp = 320, heightDp = 480, showBackground = true) +@Composable +fun PreviewAttachmentOptionsComponentSmallScreen() { + Surface { + Box( + modifier = Modifier.height(KeyboardHeight.default), + contentAlignment = Alignment.BottomCenter + ) { + AttachmentOptionsComponent( + {}, + isFileSharingEnabled = true, + tempWritableImageUri = null, + tempWritableVideoUri = null, + onRecordAudioMessageClicked = {}, + ) + } + } +} + +@Preview(name = "Normal Screen", widthDp = 360, heightDp = 640) +@Composable +fun PreviewAttachmentOptionsComponentNormalScreen() { + Surface { + Box( + modifier = Modifier.height(KeyboardHeight.default), + contentAlignment = Alignment.BottomCenter + ) { + AttachmentOptionsComponent( + {}, + isFileSharingEnabled = true, + tempWritableImageUri = null, + tempWritableVideoUri = null, + onRecordAudioMessageClicked = {}, + ) + } + } +} + +@Preview(name = "Tablet Screen", widthDp = 600, heightDp = 960) +@Composable +fun PreviewAttachmentOptionsComponentTabledScreen() { + Surface { + Box( + modifier = Modifier.height(KeyboardHeight.default), + contentAlignment = Alignment.BottomCenter + ) { + AttachmentOptionsComponent( + {}, + isFileSharingEnabled = true, + tempWritableImageUri = null, + tempWritableVideoUri = null, + onRecordAudioMessageClicked = {}, + ) + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 53ececb3d29..d8e4a4dd475 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,7 +40,7 @@ compose-material = "1.6.0-alpha05" compose-activity = "1.7.2" compose-compiler = "1.5.2" compose-constraint = "1.0.1" -compose-material3 = "1.1.1" +compose-material3 = "1.2.0-alpha09" compose-navigation = "2.6.0" compose-destinations = "1.9.40-beta"