diff --git a/core/ui/src/main/java/org/signal/core/ui/compose/SignalPreviews.kt b/core/ui/src/main/java/org/signal/core/ui/compose/SignalPreviews.kt index 54586d09e3..47bfd0f6d4 100644 --- a/core/ui/src/main/java/org/signal/core/ui/compose/SignalPreviews.kt +++ b/core/ui/src/main/java/org/signal/core/ui/compose/SignalPreviews.kt @@ -14,6 +14,7 @@ private const val SMALL_FOLDABLE_PORTRAIT = "spec:width=620dp,height=720dp,orien private const val SMALL_FOLDABLE_LANDSCAPE = "spec:width=720dp,height=620dp,orientation=landscape" private const val FOLDABLE_PORTRAIT = "spec:width=850dp,height=881dp,orientation=portrait" private const val FOLDABLE_LANDSCAPE = "spec:width=881dp,height=850dp,orientation=landscape" +private const val SMALL_TABLET_LANDSCAPE = "spec:width=960dp,height=600dp,orientation=landscape" private const val TABLET_PORTRAIT = "spec:width=800dp,height=1280dp,orientation=portrait" private const val TABLET_LANDSCAPE = "spec:width=1280dp,height=800dp,orientation=landscape" @@ -57,6 +58,13 @@ annotation class FoldablePortraitNightPreview @Preview(name = "foldable landscape (day)", uiMode = Configuration.UI_MODE_NIGHT_NO, device = FOLDABLE_LANDSCAPE) annotation class FoldableLandscapeDayPreview +/** + * A small tablet in landscape: expanded width, but the shortest height of any non-phone window, which is where + * fixed-height content runs out of room first. + */ +@Preview(name = "small tablet landscape (day)", uiMode = Configuration.UI_MODE_NIGHT_NO, device = SMALL_TABLET_LANDSCAPE) +annotation class SmallTabletLandscapeDayPreview + @Preview(name = "tablet portrait (day)", uiMode = Configuration.UI_MODE_NIGHT_NO, device = TABLET_PORTRAIT) annotation class TabletPortraitDayPreview diff --git a/feature/registration/src/main/java/org/signal/registration/screens/linkaccount/LinkAccountScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/linkaccount/LinkAccountScreen.kt index 9c8285a7dd..5341428aff 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/linkaccount/LinkAccountScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/linkaccount/LinkAccountScreen.kt @@ -78,6 +78,7 @@ import org.signal.core.ui.compose.Previews import org.signal.core.ui.compose.QrCode import org.signal.core.ui.compose.QrCodeData import org.signal.core.ui.compose.SignalIcons +import org.signal.core.ui.compose.SmallTabletLandscapeDayPreview import org.signal.core.ui.fonts.SignalSymbols import org.signal.core.ui.fonts.SignalSymbols.SignalSymbol import org.signal.core.ui.rememberWindowBreakpoint @@ -240,8 +241,9 @@ private fun TwoPane( FirstPaneContent( onEvent = onEvent, modifier = Modifier - .padding(paddingValues) .weight(1f) + .verticalScroll(rememberScrollState()) + .padding(paddingValues) ) }, secondPane = { paddingValues -> @@ -369,10 +371,17 @@ private fun QrCodeContent( } } - Box( + BoxWithConstraints( contentAlignment = if (isInOverlay) Alignment.Center else Alignment.CenterEnd, modifier = modifier ) { + val outerBorderSize = getQrOuterBorderSize(isInOverlay, overlayMaxWidth) + + // The breakpoint sizes are fixed, so scale them down uniformly when the window can't fit them (e.g. a landscape small tablet) + val scale = (minOf(maxWidth, maxHeight) / outerBorderSize).coerceAtMost(1f) + val innerBorderSize = getQrInnerBorderSize(isInOverlay, overlayMaxWidth) * scale + val qrCodeSize = getQrCodeSize(isInOverlay, overlayMaxWidth) * scale + with(sharedTransitionScope) { Box( contentAlignment = Alignment.Center, @@ -382,8 +391,8 @@ private fun QrCodeContent( animatedVisibilityScope = animatedVisibilityScope, boundsTransform = qrBoundsTransform ) - .size(getQrOuterBorderSize(isInOverlay, overlayMaxWidth)) - .background(color = colorResource(org.signal.core.ui.R.color.signal_light_colorPrimary), shape = RoundedCornerShape(if (isPhone) 48.dp else 64.dp)) + .size(outerBorderSize * scale) + .background(color = colorResource(org.signal.core.ui.R.color.signal_light_colorPrimary), shape = RoundedCornerShape((if (isPhone) 48.dp else 64.dp) * scale)) ) { AnimatedContent( targetState = state.qrCodeState, @@ -393,8 +402,8 @@ private fun QrCodeContent( animatedVisibilityScope = animatedVisibilityScope, boundsTransform = qrBoundsTransform ) - .size(getQrInnerBorderSize(isInOverlay, overlayMaxWidth)) - .background(color = Color.White, shape = RoundedCornerShape(if (isPhone) 26.dp else 24.dp)) + .size(innerBorderSize) + .background(color = Color.White, shape = RoundedCornerShape((if (isPhone) 26.dp else 24.dp) * scale)) ) { target -> Box( contentAlignment = Alignment.Center, @@ -402,7 +411,7 @@ private fun QrCodeContent( ) { when (target) { QrState.Failed -> QrCodeFailed(onEvent) - is QrState.Loaded -> QrCodeDisplay(target.qrCodeData, isInOverlay, overlayMaxWidth, qrBoundsTransform, sharedTransitionScope, animatedVisibilityScope) + is QrState.Loaded -> QrCodeDisplay(target.qrCodeData, qrCodeSize, qrBoundsTransform, sharedTransitionScope, animatedVisibilityScope) QrState.Loading -> QrCodeLoading() QrState.Scanned -> QrCodeScanned() } @@ -441,8 +450,7 @@ private fun QrCodeContent( @Composable private fun QrCodeDisplay( qrCodeData: QrCodeData, - isInOverlay: Boolean, - overlayMaxWidth: Dp?, + size: Dp, boundsTransform: BoundsTransform, sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope @@ -457,7 +465,7 @@ private fun QrCodeDisplay( animatedVisibilityScope = animatedVisibilityScope, boundsTransform = boundsTransform ) - .size(getQrCodeSize(isInOverlay, overlayMaxWidth)) + .size(size) ) } } @@ -677,6 +685,7 @@ private fun DontHaveSignal(onEvent: (LinkAccountScreenEvent) -> Unit) { } @AllDevicePreviews +@SmallTabletLandscapeDayPreview @Composable private fun LinkAccountScreenPreview() { var displayQrOverlay by remember { mutableStateOf(false) }