From 337afb11dbb6bb82825651eb225cf01d1eeeb4ad Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 4 Jun 2026 12:26:30 -0300 Subject: [PATCH] Add screen size check for link and sync. --- .../screens/welcome/WelcomeScreen.kt | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt index c16f05effa..43066ac7ae 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt @@ -7,6 +7,8 @@ package org.signal.registration.screens.welcome +import android.content.pm.PackageManager +import android.util.DisplayMetrics import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -35,22 +37,26 @@ import androidx.compose.material3.TextButton import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.window.layout.WindowMetricsCalculator import kotlinx.coroutines.CoroutineScope import org.signal.core.ui.WindowBreakpoint import org.signal.core.ui.compose.AllDevicePreviews @@ -67,6 +73,8 @@ import org.signal.registration.R import org.signal.registration.screens.RegistrationScaffold import org.signal.registration.screens.attachDebugLogHelper import org.signal.registration.test.TestTags +import kotlin.math.pow +import kotlin.math.sqrt /** * Welcome screen for the registration flow. @@ -74,14 +82,15 @@ import org.signal.registration.test.TestTags */ @Composable fun WelcomeScreen( - isLinkAndSyncAvailable: Boolean = false, onEvent: (WelcomeScreenEvents) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + isLinkAndSyncAvailable: Boolean = false ) { var showBottomSheet by remember { mutableStateOf(false) } val windowBreakpoint = rememberWindowBreakpoint() val onRestoreOrTransferClick = { showBottomSheet = true } val onTermsAndPrivacyClick = { onEvent(WelcomeScreenEvents.ViewTermsAndPrivacy) } + val displayLinkAsPrimaryOption by rememberDisplayLinkAndSyncAsPrimaryPath(isLinkAndSyncAvailable) when (windowBreakpoint) { is WindowBreakpoint.Small -> { @@ -104,7 +113,7 @@ fun WelcomeScreen( is WindowBreakpoint.Large -> { LargeLayout( - isLinkAndSyncAvailable = isLinkAndSyncAvailable, + displayLinkAsPrimaryOption = displayLinkAsPrimaryOption, onEvent = onEvent, onTermsAndPrivacyClick = onTermsAndPrivacyClick, onRestoreOrTransferClick = onRestoreOrTransferClick, @@ -240,7 +249,7 @@ private fun MediumLayout( @Composable private fun LargeLayout( - isLinkAndSyncAvailable: Boolean, + displayLinkAsPrimaryOption: Boolean, onEvent: (WelcomeScreenEvents) -> Unit, onTermsAndPrivacyClick: () -> Unit, onRestoreOrTransferClick: () -> Unit, @@ -285,7 +294,7 @@ private fun LargeLayout( .padding(bottom = 8.dp) ) - if (isLinkAndSyncAvailable) { + if (displayLinkAsPrimaryOption) { SecondaryDeviceCallToActionButtons( onEvent = onEvent ) @@ -519,6 +528,29 @@ private fun RestoreActionRow( } } +private const val TABLET_MIN_DIAGONAL_INCHES = 7f + +@Composable +private fun rememberDisplayLinkAndSyncAsPrimaryPath(isLinkAndSyncAvailable: Boolean): State { + val context = LocalContext.current + val hasHinge = currentWindowAdaptiveInfo().windowPosture.hingeList.isNotEmpty() + + val supportsTelephony = remember(context) { + context.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) + } + + val isLargeDevice = remember(context) { + val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(context) + val dpi = metrics.density * DisplayMetrics.DENSITY_DEFAULT + val widthInches = metrics.bounds.width() / dpi + val heightInches = metrics.bounds.height() / dpi + sqrt(widthInches.pow(2) + heightInches.pow(2)) >= TABLET_MIN_DIAGONAL_INCHES + } + + // A hinge means a foldable, which counts as a phone rather than a tablet. + return rememberUpdatedState(isLinkAndSyncAvailable && (!supportsTelephony || (isLargeDevice && !hasHinge))) +} + @AllDevicePreviews @Composable private fun WelcomeScreenPreview() {