diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/AccountNumberView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/AccountNumberView.kt index c4fd06897351..689ff5dfd552 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/AccountNumberView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/AccountNumberView.kt @@ -1,15 +1,21 @@ package net.mullvad.mullvadvpn.compose.component import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import net.mullvad.mullvadvpn.lib.common.util.groupPasswordModeWithSpaces import net.mullvad.mullvadvpn.lib.common.util.groupWithSpaces @Composable -fun AccountNumberView(accountNumber: String, doObfuscateWithPasswordDots: Boolean) { +fun AccountNumberView( + accountNumber: String, + doObfuscateWithPasswordDots: Boolean, + modifier: Modifier = Modifier +) { InformationView( content = if (doObfuscateWithPasswordDots) accountNumber.groupPasswordModeWithSpaces() else accountNumber.groupWithSpaces(), + modifier = modifier, whenMissing = MissingPolicy.SHOW_SPINNER ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CopyableObfuscationView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CopyableObfuscationView.kt index 71ff694f15fd..571985c0be68 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CopyableObfuscationView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CopyableObfuscationView.kt @@ -1,13 +1,12 @@ package net.mullvad.mullvadvpn.compose.component import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext @@ -30,16 +29,17 @@ fun CopyableObfuscationView(content: String) { val context = LocalContext.current val shouldObfuscated = remember { mutableStateOf(true) } - Row(verticalAlignment = Alignment.CenterVertically) { + Row(verticalAlignment = CenterVertically) { AccountNumberView( accountNumber = content, - doObfuscateWithPasswordDots = shouldObfuscated.value + doObfuscateWithPasswordDots = shouldObfuscated.value, + modifier = Modifier.weight(1f) ) - Spacer(modifier = Modifier.weight(1f)) AnimatedIconButton( defaultIcon = painterResource(id = R.drawable.icon_hide), secondaryIcon = painterResource(id = R.drawable.icon_show), isToggleButton = true, + modifier = Modifier.padding(start = Dimens.smallPadding, end = Dimens.sideMargin), onClick = { shouldObfuscated.value = shouldObfuscated.value.not() } ) AnimatedIconButton( @@ -48,7 +48,7 @@ fun CopyableObfuscationView(content: String) { secondaryIconColorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.inversePrimary), isToggleButton = false, - modifier = Modifier.padding(start = Dimens.sideMargin, end = Dimens.sideMargin), + modifier = Modifier.padding(end = Dimens.sideMargin), onClick = { context.copyToClipboard( content = content, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt index 2d45a02f0cdb..7eddace4c28f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt @@ -1,15 +1,19 @@ package net.mullvad.mullvadvpn.compose.component +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import net.mullvad.mullvadvpn.compose.theme.Dimens +import net.mullvad.mullvadvpn.compose.theme.typeface.TypeScale.TextMedium +import net.mullvad.mullvadvpn.compose.theme.typeface.TypeScale.TextSmall @Preview @Composable @@ -24,48 +28,59 @@ private fun PreviewEmptyInformationView() { } @Composable -fun InformationView(content: String, whenMissing: MissingPolicy = MissingPolicy.SHOW_VIEW) { +fun InformationView( + content: String, + modifier: Modifier = Modifier, + whenMissing: MissingPolicy = MissingPolicy.SHOW_VIEW, + maxLines: Int = 1 +) { return if (content.isNotEmpty()) { - Text( + AutoResizeText( style = MaterialTheme.typography.titleSmall, text = content, + minTextSize = TextSmall, + maxTextSize = TextMedium, + maxLines = maxLines, modifier = - Modifier.padding( + modifier.padding( start = Dimens.sideMargin, - end = Dimens.sideMargin, top = Dimens.smallPadding, - bottom = Dimens.mediumPadding + bottom = Dimens.smallPadding ) ) } else { when (whenMissing) { MissingPolicy.SHOW_VIEW -> { - Text( + AutoResizeText( style = MaterialTheme.typography.titleMedium, text = content, + minTextSize = TextSmall, + maxTextSize = TextMedium, + maxLines = maxLines, modifier = - Modifier.padding( + modifier.padding( start = Dimens.sideMargin, - end = Dimens.sideMargin, top = Dimens.smallPadding, - bottom = Dimens.mediumPadding + bottom = Dimens.smallPadding ) ) } MissingPolicy.HIDE_VIEW -> {} MissingPolicy.SHOW_SPINNER -> { - CircularProgressIndicator( - modifier = - Modifier.padding( - start = Dimens.sideMargin, - end = Dimens.sideMargin, - top = Dimens.smallPadding, - bottom = Dimens.mediumPadding - ) - .height(Dimens.loadingSpinnerSizeMedium) - .width(Dimens.loadingSpinnerSizeMedium), - color = MaterialTheme.colorScheme.onSecondary - ) + Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) { + CircularProgressIndicator( + modifier = + Modifier.padding( + start = Dimens.sideMargin, + top = Dimens.smallPadding, + bottom = Dimens.smallPadding + ) + .height(Dimens.loadingSpinnerSizeMedium) + .width(Dimens.loadingSpinnerSizeMedium), + color = MaterialTheme.colorScheme.onSecondary + ) + Spacer(modifier = Modifier.weight(1f)) + } } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt index 9f8527a021c6..79112f58ff75 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt @@ -3,7 +3,12 @@ package net.mullvad.mullvadvpn.compose.component import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.TextStyle @@ -13,6 +18,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.sp + +internal val DEFAULT_TEXT_STEP = 1.sp @Composable fun CapsText( @@ -52,3 +60,38 @@ fun CapsText( style = style, ) } + +@Composable +fun AutoResizeText( + text: String, + minTextSize: TextUnit, + maxTextSize: TextUnit, + modifier: Modifier = Modifier, + textSizeStep: TextUnit = DEFAULT_TEXT_STEP, + style: TextStyle = LocalTextStyle.current, + maxLines: Int = Int.MAX_VALUE, +) { + var adjustedFontSize by remember { mutableStateOf(maxTextSize.value) } + var isReadyToDraw by remember { mutableStateOf(false) } + + Text( + text = text, + maxLines = maxLines, + style = style, + fontSize = adjustedFontSize.sp, + onTextLayout = { + if (it.didOverflowHeight && isReadyToDraw.not()) { + val nextFontSizeValue = adjustedFontSize - textSizeStep.value + if (nextFontSizeValue <= minTextSize.value) { + adjustedFontSize = minTextSize.value + isReadyToDraw = true + } else { + adjustedFontSize = nextFontSizeValue + } + } else { + isReadyToDraw = true + } + }, + modifier = modifier.drawWithContent { if (isReadyToDraw) drawContent() } + ) +}