diff --git a/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinBoard.kt b/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinBoard.kt index 4948a34..46a6be3 100644 --- a/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinBoard.kt +++ b/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinBoard.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.xinto.mauth.R import com.xinto.mauth.ui.theme.MauthTheme @@ -27,12 +28,14 @@ import com.xinto.mauth.ui.theme.MauthTheme @Composable fun PinBoard( modifier: Modifier = Modifier, + horizontalButtonSpace: Dp = 16.dp, + minButtonSize: Dp = PinButtonDefaults.PinButtonNormalMinSize, state: PinBoardState = rememberPinBoardState() ) { FlowRow( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically), - horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally), + horizontalArrangement = Arrangement.spacedBy(horizontalButtonSpace, Alignment.CenterHorizontally), maxItemsInEachRow = 3 ) { state.buttons.forEach { button -> @@ -40,7 +43,8 @@ fun PinBoard( is PinBoardState.PinBoardButton.Number -> { PinButton( modifier = Modifier.weight(1f), - onClick = { state.onNumberClick(button.number) } + onClick = { state.onNumberClick(button.number) }, + minButtonSize = minButtonSize ) { Text(button.toString()) } @@ -57,9 +61,10 @@ fun PinBoard( else -> throw NoSuchElementException() }, onLongClick = - if (button is PinBoardState.PinBoardButton.Backspace) - state.onBackspaceLongClick - else null + if (button is PinBoardState.PinBoardButton.Backspace) + state.onBackspaceLongClick + else null, + minButtonSize = minButtonSize ) { Icon( modifier = Modifier.fillMaxSize(0.4f).aspectRatio(1f), @@ -76,7 +81,7 @@ fun PinBoard( } } is PinBoardState.PinBoardButton.Empty -> { - Spacer(Modifier.aspectRatio(1f).weight(1f).size(PinButtonDefaults.PinButtonMinSize)) + Spacer(Modifier.aspectRatio(1f).weight(1f).size(minButtonSize)) } } } diff --git a/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinButton.kt b/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinButton.kt index 1d2f37c..c72be44 100644 --- a/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinButton.kt +++ b/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinButton.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.xinto.mauth.ui.component.Animatable import kotlinx.coroutines.CancellationException @@ -48,6 +49,7 @@ fun PrimaryPinButton( enabled: Boolean = true, colors: PinButtonColors = PinButtonDefaults.primaryPinButtonColors(), shapes: PinButtonShapes = PinButtonDefaults.plainPinButtonShapes(), + minButtonSize: Dp = PinButtonDefaults.PinButtonNormalMinSize, content: @Composable () -> Unit ) = PinButton( onClick = onClick, @@ -56,6 +58,7 @@ fun PrimaryPinButton( enabled = enabled, colors = colors, shapes = shapes, + minButtonSize = minButtonSize, content = content ) @@ -67,18 +70,19 @@ fun PinButton( enabled: Boolean = true, colors: PinButtonColors = PinButtonDefaults.plainPinButtonColors(), shapes: PinButtonShapes = PinButtonDefaults.plainPinButtonShapes(), + minButtonSize: Dp = PinButtonDefaults.PinButtonNormalMinSize, content: @Composable () -> Unit ) { val interactionSource = remember { MutableInteractionSource() } - val shape by shapes.getButtonShape(interactionSource) + val shape by shapes.getButtonShape(interactionSource, minButtonSize) val backgroundColor by colors.getBackgroundColor(interactionSource) val contentColor by colors.getForegroundColor(interactionSource) Box( modifier = modifier .aspectRatio(1f) .sizeIn( - minWidth = PinButtonDefaults.PinButtonMinSize, - minHeight = PinButtonDefaults.PinButtonMinSize, + minWidth = minButtonSize, + minHeight = minButtonSize, ) .graphicsLayer { clip = true @@ -106,7 +110,8 @@ fun PinButton( object PinButtonDefaults { - val PinButtonMinSize = 72.dp + val PinButtonSmallMinSize = 56.dp + val PinButtonNormalMinSize = 72.dp const val AnimationDurationPress = 200 const val AnimationDurationRelease = 150 @@ -190,14 +195,14 @@ data class PinButtonShapes( ) { @Composable - fun getButtonShape(interactionSource: InteractionSource): State { + fun getButtonShape(interactionSource: InteractionSource, minButtonSize: Dp = PinButtonDefaults.PinButtonNormalMinSize): State { val density = LocalDensity.current val size = with(density) { - val shapeSize = PinButtonDefaults.PinButtonMinSize.toPx() + val shapeSize = minButtonSize.toPx() Size(shapeSize, shapeSize) } - val animatable = remember(density, size) { + val animatable = remember(density, size, minButtonSize) { Animatable(shape, density, size) } return animatePressValue( diff --git a/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinScaffold.kt b/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinScaffold.kt index 5eac848..495e6ab 100644 --- a/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinScaffold.kt +++ b/app/src/main/java/com/xinto/mauth/ui/component/pinboard/PinScaffold.kt @@ -4,11 +4,14 @@ import android.content.res.Configuration 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.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.sizeIn import androidx.compose.material3.FabPosition import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme @@ -18,9 +21,11 @@ import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -40,6 +45,7 @@ fun PinScaffold( contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, description: (@Composable () -> Unit)? = null, error: Boolean = false, + useSmallButtons: Boolean = false, codeLength: Int, ) { Scaffold( @@ -53,44 +59,116 @@ fun PinScaffold( contentColor = contentColor, contentWindowInsets = contentWindowInsets, ) { - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - .padding(it) - .padding(40.dp) - .padding(bottom = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom), - horizontalAlignment = Alignment.CenterHorizontally - ) { - if (description != null) { - Spacer(modifier = Modifier.weight(1f)) + val orientation = LocalConfiguration.current.orientation + val minButtonSize = remember(useSmallButtons) { + if (useSmallButtons) { + PinButtonDefaults.PinButtonSmallMinSize + } else { + PinButtonDefaults.PinButtonNormalMinSize + } + } + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + Row( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .padding(it) + .padding(horizontal = 20.dp), + horizontalArrangement = Arrangement.SpaceAround, + verticalAlignment = Alignment.CenterVertically + ) { Box( - modifier = Modifier.fillMaxWidth(), - contentAlignment = Alignment.Center + modifier = Modifier + .fillMaxWidth(0.5f) + .fillMaxHeight() ) { - CompositionLocalProvider( - LocalTextStyle provides MaterialTheme.typography.headlineMedium.copy( - textAlign = TextAlign.Center - ) + Column( + modifier = Modifier.fillMaxHeight(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally ) { - description() + if (description != null) { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.headlineMedium.copy( + textAlign = TextAlign.Center + ) + ) { + description() + } + } + Spacer(modifier = Modifier.height(32.dp)) + } + PinDisplay( + modifier = Modifier + .fillMaxWidth(0.5f), + length = codeLength, + error = error, + ) } } - Spacer(modifier = Modifier.weight(1f)) + val pinBoardHorizontalPadding = 16.dp + val horizontalButtonSpace = 16.dp + val totalPadding = (pinBoardHorizontalPadding * 2) + (horizontalButtonSpace * 2) + val maxBoxWidth = remember(minButtonSize) { + val buttonsInRow = 3 + (minButtonSize * buttonsInRow) + totalPadding + } + Box( + modifier = Modifier.sizeIn(maxWidth = maxBoxWidth), + contentAlignment = Alignment.Center + ) { + PinBoard( + modifier = Modifier.padding(horizontal = pinBoardHorizontalPadding), + horizontalButtonSpace = horizontalButtonSpace, + minButtonSize = minButtonSize, + state = state + ) + } } - PinDisplay( - modifier = Modifier - .fillMaxWidth(), - length = codeLength, - error = error, - ) - PinBoard( + } else { + + Column( modifier = Modifier - .padding(horizontal = 8.dp) - .padding(top = 32.dp), - state = state - ) + .fillMaxWidth() + .fillMaxHeight() + .padding(it) + .padding(start = 40.dp, top = 40.dp, end = 40.dp, bottom = 56.dp), + verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom), + horizontalAlignment = Alignment.CenterHorizontally + ) { + if (description != null) { + Spacer(modifier = Modifier.weight(1f)) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.headlineMedium.copy( + textAlign = TextAlign.Center + ) + ) { + description() + } + } + Spacer(modifier = Modifier.weight(1f)) + } + PinDisplay( + modifier = Modifier + .fillMaxWidth(), + length = codeLength, + error = error, + ) + PinBoard( + modifier = Modifier + .padding(horizontal = 8.dp) + .padding(top = 32.dp), + state = state + ) + } } } } diff --git a/app/src/main/java/com/xinto/mauth/ui/screen/pinremove/PinRemoveScreen.kt b/app/src/main/java/com/xinto/mauth/ui/screen/pinremove/PinRemoveScreen.kt index 2b57bc0..a608167 100644 --- a/app/src/main/java/com/xinto/mauth/ui/screen/pinremove/PinRemoveScreen.kt +++ b/app/src/main/java/com/xinto/mauth/ui/screen/pinremove/PinRemoveScreen.kt @@ -1,12 +1,15 @@ package com.xinto.mauth.ui.screen.pinremove +import android.content.res.Configuration import androidx.activity.compose.BackHandler import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -45,25 +48,25 @@ fun PinRemoveScreen( onNumberDelete: () -> Unit, onAllDelete: () -> Unit, ) { + val orientation = LocalConfiguration.current.orientation PinScaffold( codeLength = state.code.length, error = state is PinRemoveScreenState.Error, topBar = { - LargeTopAppBar( - title = { - Text(stringResource(R.string.pinremove_title)) - }, - navigationIcon = { - IconButton(onClick = onBack) { - Icon( - painter = painterResource(R.drawable.ic_arrow_back), - contentDescription = null - ) - } - } - ) + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + TopAppBar( + title = { AppBarTitle() }, + navigationIcon = { AppBarNavigationIcon(onBack) } + ) + } else { + LargeTopAppBar( + title = { AppBarTitle() }, + navigationIcon = { AppBarNavigationIcon(onBack) } + ) + } }, description = null, + useSmallButtons = orientation == Configuration.ORIENTATION_LANDSCAPE, state = rememberPinBoardState( showEnter = true, onNumberClick = onNumberEnter, @@ -72,4 +75,19 @@ fun PinRemoveScreen( onBackspaceLongClick = onAllDelete ) ) +} + +@Composable +fun AppBarTitle() { + Text(stringResource(R.string.pinremove_title)) +} + +@Composable +fun AppBarNavigationIcon(onBack: () -> Unit) { + IconButton(onClick = onBack) { + Icon( + painter = painterResource(R.drawable.ic_arrow_back), + contentDescription = null + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/xinto/mauth/ui/screen/pinsetup/PinSetupScreen.kt b/app/src/main/java/com/xinto/mauth/ui/screen/pinsetup/PinSetupScreen.kt index 03a7dd6..04ba7fc 100644 --- a/app/src/main/java/com/xinto/mauth/ui/screen/pinsetup/PinSetupScreen.kt +++ b/app/src/main/java/com/xinto/mauth/ui/screen/pinsetup/PinSetupScreen.kt @@ -1,5 +1,6 @@ package com.xinto.mauth.ui.screen.pinsetup +import android.content.res.Configuration import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedContent import androidx.compose.animation.fadeIn @@ -9,8 +10,10 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -63,37 +66,25 @@ fun PinSetupScreen( onNumberDelete: () -> Unit, onAllDelete: () -> Unit, ) { + val orientation = LocalConfiguration.current.orientation PinScaffold( codeLength = code.length, error = error, topBar = { - LargeTopAppBar( - title = { - AnimatedContent( - targetState = state, - label = "PinSetupDescription", - transitionSpec = { - fadeIn() togetherWith fadeOut() - } - ) { - val resource = when (it) { - is PinSetupScreenState.Initial -> R.string.pinsetup_title_create - is PinSetupScreenState.Confirm -> R.string.pinsetup_title_confirm - } - Text(stringResource(resource)) - } - }, - navigationIcon = { - IconButton(onClick = onPrevious) { - Icon( - painter = painterResource(R.drawable.ic_arrow_back), - contentDescription = null - ) - } - } - ) + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + TopAppBar( + title = { AppBarTitle(state) }, + navigationIcon = { AppBarNavigationIcon(onPrevious) } + ) + } else { + LargeTopAppBar( + title = { AppBarTitle(state) }, + navigationIcon = { AppBarNavigationIcon(onPrevious) } + ) + } }, description = null, + useSmallButtons = orientation == Configuration.ORIENTATION_LANDSCAPE, state = rememberPinBoardState( showEnter = true, onNumberClick = onNumberEnter, @@ -102,4 +93,31 @@ fun PinSetupScreen( onBackspaceLongClick = onAllDelete ) ) +} + +@Composable +fun AppBarTitle(state: PinSetupScreenState) { + AnimatedContent( + targetState = state, + label = "PinSetupDescription", + transitionSpec = { + fadeIn() togetherWith fadeOut() + } + ) { + val resource = when (it) { + is PinSetupScreenState.Initial -> R.string.pinsetup_title_create + is PinSetupScreenState.Confirm -> R.string.pinsetup_title_confirm + } + Text(stringResource(resource)) + } +} + +@Composable +fun AppBarNavigationIcon(onPrevious: () -> Unit) { + IconButton(onClick = onPrevious) { + Icon( + painter = painterResource(R.drawable.ic_arrow_back), + contentDescription = null + ) + } } \ No newline at end of file