Update payment menu design

This commit is contained in:
Aleksandras Kostarevas 2024-06-14 15:19:14 +03:00
parent 513c08d39f
commit 13f81556ad
5 changed files with 287 additions and 115 deletions

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22,12l-4,0l-3,9l-6,-18l-3,9l-4,0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,47 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<group>
<clip-path
android:pathData="M0,0h128v128h-128z"/>
<path
android:pathData="M114.5,49.5C114.5,56.96 108.58,63 101.28,63C93.98,63 88.06,56.96 88.06,49.5C88.06,42.04 93.98,36 101.28,36C108.58,36 114.5,42.04 114.5,49.5ZZM97.43,52.22C96.08,50.84 95.4,50.15 95.4,49.29C95.4,48.43 96.08,47.73 97.43,46.35L98.54,45.22C99.89,43.84 100.57,43.14 101.41,43.14C102.25,43.14 102.93,43.84 104.29,45.22L105.39,46.35C106.75,47.73 107.43,48.43 107.43,49.29C107.43,50.15 106.75,50.84 105.39,52.22L104.29,53.35C102.93,54.74 102.25,55.43 101.41,55.43C100.57,55.43 99.89,54.74 98.54,53.35L97.43,52.22ZZM32.14,43.96C32.51,43.96 32.82,43.65 32.82,43.27L32.82,38.08C32.82,37.69 32.51,37.38 32.14,37.38L14.18,37.38C13.8,37.38 13.5,37.69 13.5,38.08L13.5,60.92C13.5,61.31 13.8,61.62 14.18,61.62L20.28,61.62C20.65,61.62 20.96,61.31 20.96,60.92L20.96,54.55C20.96,54.17 21.26,53.86 21.63,53.86L29.84,53.86C30.21,53.86 30.51,53.55 30.51,53.17L30.51,47.98C30.51,47.6 30.21,47.28 29.84,47.28L21.63,47.28C21.26,47.28 20.96,46.97 20.96,46.59L20.96,44.65C20.96,44.27 21.26,43.96 21.63,43.96L32.14,43.96ZZM47.9,62.03L48,62.03C55.25,62.03 59.69,58.08 59.69,50.57L59.69,38.08C59.69,37.69 59.39,37.38 59.01,37.38L52.91,37.38C52.54,37.38 52.24,37.69 52.24,38.08L52.24,49.88C52.24,52.44 51.15,54.69 48,54.69L47.9,54.69C44.78,54.69 43.66,52.44 43.66,49.88L43.66,38.08C43.66,37.69 43.36,37.38 42.98,37.38L36.88,37.38C36.51,37.38 36.21,37.69 36.21,38.08L36.21,50.57C36.21,58.08 40.65,62.03 47.9,62.03ZZM63.08,38.08C63.08,37.69 63.38,37.38 63.76,37.38L85.65,37.38C86.03,37.38 86.33,37.69 86.33,38.08L86.33,43.37C86.33,43.76 86.03,44.06 85.65,44.06L79.11,44.06C78.74,44.06 78.43,44.38 78.43,44.76L78.43,60.92C78.43,61.31 78.13,61.62 77.76,61.62L71.66,61.62C71.28,61.62 70.98,61.31 70.98,60.92L70.98,44.76C70.98,44.38 70.68,44.06 70.3,44.06L63.76,44.06C63.38,44.06 63.08,43.76 63.08,43.37L63.08,38.08ZZ"
android:fillColor="#FFFFFF"
android:fillType="evenOdd"/>
<path
android:pathData="M35,72L40.54,72A2,2 0,0 1,42.54 74L42.54,79.6A2,2 0,0 1,40.54 81.6L35,81.6A2,2 0,0 1,33 79.6L33,74A2,2 0,0 1,35 72z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M35,85.2L40.54,85.2A2,2 0,0 1,42.54 87.2L42.54,92.8A2,2 0,0 1,40.54 94.8L35,94.8A2,2 0,0 1,33 92.8L33,87.2A2,2 0,0 1,35 85.2z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M48.12,72L53.65,72A2,2 0,0 1,55.65 74L55.65,79.6A2,2 0,0 1,53.65 81.6L48.12,81.6A2,2 0,0 1,46.12 79.6L46.12,74A2,2 0,0 1,48.12 72z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M48.12,85.2L53.65,85.2A2,2 0,0 1,55.65 87.2L55.65,92.8A2,2 0,0 1,53.65 94.8L48.12,94.8A2,2 0,0 1,46.12 92.8L46.12,87.2A2,2 0,0 1,48.12 85.2z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M61.23,72L66.77,72A2,2 0,0 1,68.77 74L68.77,79.6A2,2 0,0 1,66.77 81.6L61.23,81.6A2,2 0,0 1,59.23 79.6L59.23,74A2,2 0,0 1,61.23 72z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M61.23,85.2L66.77,85.2A2,2 0,0 1,68.77 87.2L68.77,92.8A2,2 0,0 1,66.77 94.8L61.23,94.8A2,2 0,0 1,59.23 92.8L59.23,87.2A2,2 0,0 1,61.23 85.2z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M45.73,98.4L82.27,98.4A2,2 0,0 1,84.27 100.4L84.27,106A2,2 0,0 1,82.27 108L45.73,108A2,2 0,0 1,43.73 106L43.73,100.4A2,2 0,0 1,45.73 98.4z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M74.35,72L79.88,72A2,2 0,0 1,81.88 74L81.88,79.6A2,2 0,0 1,79.88 81.6L74.35,81.6A2,2 0,0 1,72.35 79.6L72.35,74A2,2 0,0 1,74.35 72z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M74.35,85.2L79.88,85.2A2,2 0,0 1,81.88 87.2L81.88,92.8A2,2 0,0 1,79.88 94.8L74.35,94.8A2,2 0,0 1,72.35 92.8L72.35,87.2A2,2 0,0 1,74.35 85.2z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M87.46,72L93,72A2,2 0,0 1,95 74L95,79.6A2,2 0,0 1,93 81.6L87.46,81.6A2,2 0,0 1,85.46 79.6L85.46,74A2,2 0,0 1,87.46 72z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M87.46,85.2L93,85.2A2,2 0,0 1,95 87.2L95,92.8A2,2 0,0 1,93 94.8L87.46,94.8A2,2 0,0 1,85.46 92.8L85.46,87.2A2,2 0,0 1,87.46 85.2z"
android:fillColor="#FFFFFF"/>
</group>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,11L19,11A2,2 0,0 1,21 13L21,20A2,2 0,0 1,19 22L5,22A2,2 0,0 1,3 20L3,13A2,2 0,0 1,5 11z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M7,11V7a5,5 0,0 1,9.9 -1"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -68,9 +68,9 @@
<string name="payment_failed_body">Unfortunately, your payment has failed for one reason or another. Please contact us if you need help. You can reach us at keyboard@futo.org, with the button below, or with Send Feedback in Help</string> <string name="payment_failed_body">Unfortunately, your payment has failed for one reason or another. Please contact us if you need help. You can reach us at keyboard@futo.org, with the button below, or with Send Feedback in Help</string>
<string name="payment_failed_body_ex">There may have been a problem with your payment. If it did go through, you should receive an email with a license key. Please contact us if you need help. You can reach us at keyboard@futo.org, with the button below, or with Share Feedback at the main screen.</string> <string name="payment_failed_body_ex">There may have been a problem with your payment. If it did go through, you should receive an email with a license key. Please contact us if you need help. You can reach us at keyboard@futo.org, with the button below, or with Share Feedback at the main screen.</string>
<string name="pay_via_x">Pay via <xliff:g name="paymentMethod" example="Play Store">%s</xliff:g></string> <string name="pay_via_x">Pay via <xliff:g name="paymentMethod" example="Play Store">%s</xliff:g></string>
<string name="pay">Pay</string> <string name="pay">Pay (about $4.99 + tax)</string>
<string name="i_already_paid">I already paid</string> <string name="i_already_paid">I already paid</string>
<string name="i_already_paid_2">I already paid (tap again)</string> <string name="i_already_paid_2">I already paid (tap again to confirm)</string>
<string name="remind_me_in_x">"Remind me in "</string> <string name="remind_me_in_x">"Remind me in "</string>
<string name="in_x_days">" days"</string> <string name="in_x_days">" days"</string>
<string name="developer_mode_payment_methods">You are on the Developer release, so you are seeing all payment methods</string> <string name="developer_mode_payment_methods">You are on the Developer release, so you are seeing all payment methods</string>

View File

@ -4,7 +4,8 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -13,18 +14,22 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableIntState import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
@ -32,15 +37,19 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.Center import androidx.compose.ui.Alignment.Companion.Center
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.longPreferencesKey
@ -120,7 +129,22 @@ fun useNumberOfDaysInstalled(): MutableIntState {
@Composable @Composable
fun ParagraphText(it: String, modifier: Modifier = Modifier) { fun ParagraphText(it: String, modifier: Modifier = Modifier) {
Text(it, modifier = modifier.padding(16.dp, 8.dp), style = Typography.bodyMedium, Text(it, modifier = modifier.padding(16.dp, 8.dp), style = Typography.bodyMedium,
color = MaterialTheme.colorScheme.onBackground) color = LocalContentColor.current)
}
@Composable
fun IconText(icon: Painter, title: String, body: String) {
Row(modifier = Modifier.padding(8.dp)) {
Icon(icon, contentDescription = null, modifier = Modifier
.align(Alignment.Top)
.padding(8.dp, 10.dp)
.size(with(LocalDensity.current) { Typography.titleMedium.fontSize.toDp() }))
Column(modifier = Modifier.padding(6.dp)) {
Text(title, style = Typography.titleMedium)
Spacer(modifier = Modifier.height(4.dp))
Text(body, style = Typography.bodySmall, color = LocalContentColor.current.copy(alpha = 0.8f))
}
}
} }
@Composable @Composable
@ -134,10 +158,28 @@ fun PaymentText(verbose: Boolean) {
ParagraphText(stringResource(R.string.payment_text_1_alt)) ParagraphText(stringResource(R.string.payment_text_1_alt))
} }
ParagraphText(stringResource(R.string.payment_text_2))
if(verbose) { if(verbose) {
ParagraphText(stringResource(R.string.payment_text_3)) IconText(
icon = painterResource(id = R.drawable.activity),
title = "Sustainable Development",
body = "FUTO's mission is for open-source software and non-malicious software business practices to become a sustainable income source for projects and their developers. For this reason, we are in favor of users actually paying for software."
)
IconText(
icon = painterResource(id = R.drawable.unlock),
title = "Commitment to Privacy",
body = "This app will never serve you ads or sell your data. We are not in the business of doing that."
)
/*
IconText(
icon = painterResource(id = R.drawable.code),
title = "Ongoing Work",
body = "Creating and maintaining great software requires significant resources. Your support will help us keep development going."
)
*/
} else {
ParagraphText(stringResource(R.string.payment_text_2))
} }
} }
@ -208,47 +250,42 @@ fun ConditionalUnpaidNoticeInVoiceInputWindow(onClose: (() -> Unit)? = null) {
} }
@Composable
fun MediumTitle(text: String) {
Text(
text,
modifier = Modifier.padding(8.dp),
style = Typography.titleMedium,
color = LocalContentColor.current
)
}
@Composable @Composable
@Preview @Preview
fun UnpaidNotice(openMenu: () -> Unit = { }) { fun UnpaidNotice(openMenu: () -> Unit = { }) {
Surface( PaymentSurface(isPrimary = true, title = stringResource(R.string.unpaid_title), onClick = openMenu) {
color = MaterialTheme.colorScheme.surfaceVariant, modifier = Modifier PaymentText(false)
.clickable { openMenu() }
.fillMaxWidth()
.padding(24.dp, 8.dp), shape = RoundedCornerShape(24.dp)
) {
Column(modifier = Modifier.padding(8.dp, 0.dp)) {
Spacer(modifier = Modifier.height(8.dp))
Text(
stringResource(R.string.unpaid_title),
modifier = Modifier.padding(8.dp),
style = Typography.titleMedium,
color = MaterialTheme.colorScheme.onBackground
)
PaymentText(false) Row(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
) {
Row( Box(modifier = Modifier.weight(1.0f)) {
modifier = Modifier Button(onClick = openMenu, modifier = Modifier.align(Center)) {
.padding(8.dp) Text(stringResource(R.string.pay_now))
.align(CenterHorizontally)
) {
Box(modifier = Modifier.weight(1.0f)) {
Button(onClick = openMenu, modifier = Modifier.align(Center)) {
Text(stringResource(R.string.pay_now))
}
} }
}
Box(modifier = Modifier.weight(1.0f)) { Box(modifier = Modifier.weight(1.0f)) {
Button( Button(
onClick = openMenu, colors = ButtonDefaults.buttonColors( onClick = openMenu, colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondary, containerColor = MaterialTheme.colorScheme.secondary,
contentColor = MaterialTheme.colorScheme.onSecondary contentColor = MaterialTheme.colorScheme.onSecondary
), modifier = Modifier.align(Center) ), modifier = Modifier.align(Center)
) { ) {
Text(stringResource(R.string.i_already_paid)) Text(stringResource(R.string.i_already_paid))
}
} }
} }
} }
@ -350,7 +387,47 @@ fun PaymentFailedScreen(onExit: () -> Unit = { }) {
} }
@Composable @Composable
@Preview(showBackground = true) fun PaymentSurface(isPrimary: Boolean, title: String, onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
val containerColor = if (isPrimary) {
MaterialTheme.colorScheme.surfaceVariant
} else {
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
}
val contentColor = if (isPrimary) {
MaterialTheme.colorScheme.onSurfaceVariant
} else {
MaterialTheme.colorScheme.onSurfaceVariant
}
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Center) {
Surface(
color = containerColor,
border = BorderStroke(2.dp, contentColor.copy(alpha = 0.33f)),
shape = RoundedCornerShape(24.dp),
modifier = Modifier
.padding(16.dp)
.widthIn(Dp.Unspecified, 400.dp)
.let {
if (onClick != null) {
it.clickable { onClick() }
} else {
it
}
}
) {
Column(modifier = Modifier.padding(8.dp)) {
CompositionLocalProvider(LocalContentColor provides contentColor) {
MediumTitle(title)
content()
}
}
}
}
}
@Composable
@Preview(showBackground = true, heightDp = 10000)
fun PaymentScreen( fun PaymentScreen(
navController: NavHostController = rememberNavController(), navController: NavHostController = rememberNavController(),
onExit: () -> Unit = { } onExit: () -> Unit = { }
@ -377,92 +454,107 @@ fun PaymentScreen(
ScrollableList { ScrollableList {
ScreenTitle(stringResource(R.string.payment_title), showBack = true, navController = navController) ScreenTitle(stringResource(R.string.payment_title), showBack = true, navController = navController)
PaymentText(true)
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Center) {
Icon(painterResource(id = R.drawable.keyboard_icon), contentDescription = null, tint = MaterialTheme.colorScheme.onBackground)
}
val context = LocalContext.current val context = LocalContext.current
Column(modifier = Modifier.fillMaxWidth()) {
Row(modifier = Modifier.padding(8.dp, 0.dp)) {
Button(
onClick = {
val url = runBlocking { context.getSetting(TMP_PAYMENT_URL) }
if(url.isNotBlank()) {
context.openURI(url)
} else {
val toast = Toast.makeText(context, "Payment is unsupported on this build (still WIP)", Toast.LENGTH_SHORT)
toast.show()
}
}, modifier = Modifier
.weight(1.0f)
.padding(8.dp)
) {
Text(stringResource(R.string.pay))
}
PaymentSurface(isPrimary = true, title = "Pay for FUTO Keyboard") {
PaymentText(true)
Button( Button(
onClick = { onClick = {
counter.intValue += 1 val url = runBlocking { context.getSetting(TMP_PAYMENT_URL) }
if(counter.intValue == 2) { if(url.isNotBlank()) {
onAlreadyPaid() context.openURI(url)
} } else {
}, colors = ButtonDefaults.buttonColors( val toast = Toast.makeText(context, "Payment is unsupported on this build", Toast.LENGTH_SHORT)
containerColor = MaterialTheme.colorScheme.secondary, toast.show()
contentColor = MaterialTheme.colorScheme.onSecondary }
), modifier = Modifier },
.weight(1.0f) modifier = Modifier
.padding(8.dp) .fillMaxWidth()
) { .padding(8.dp)
Text(stringResource( ) {
when(counter.intValue) { Text(stringResource(R.string.pay))
}
}
PaymentSurface(isPrimary = false, title = "Already paid?") {
ParagraphText(it = "If you already paid for FUTO Keyboard or FUTO Voice Input, tap below.")
Button(
onClick = {
counter.intValue += 1
if (counter.intValue == 2) {
onAlreadyPaid()
}
}, colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
), modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Text(
stringResource(
when (counter.intValue) {
0 -> R.string.i_already_paid 0 -> R.string.i_already_paid
else -> R.string.i_already_paid_2 else -> R.string.i_already_paid_2
}) }
) )
} )
} }
}
if (reminderTimeIsUp) {
PaymentSurface(isPrimary = false, title = "Remind later") {
ParagraphText("This will hide the reminder in the settings screen for the period of days entered.")
if (reminderTimeIsUp) {
val lastValidRemindValue = remember { mutableFloatStateOf(5.0f) } val lastValidRemindValue = remember { mutableFloatStateOf(5.0f) }
val remindDays = remember { mutableStateOf("5") } val remindDays = remember { mutableStateOf("5") }
Row( val coroutineScope = rememberCoroutineScope()
modifier = Modifier Button(
.align(CenterHorizontally) onClick = {
.padding(16.dp, 0.dp) coroutineScope.launch {
.fillMaxWidth() pushNoticeReminderTime(context, lastValidRemindValue.floatValue)
) {
val coroutineScope = rememberCoroutineScope()
OutlinedButton(
onClick = {
coroutineScope.launch {
pushNoticeReminderTime(context, lastValidRemindValue.floatValue)
}
onExit()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.remind_me_in_x))
Surface(color = MaterialTheme.colorScheme.primaryContainer) {
BasicTextField(
value = remindDays.value,
onValueChange = {
remindDays.value = it
it.toFloatOrNull()?.let { lastValidRemindValue.floatValue = it }
},
modifier = Modifier
.width(32.dp)
.background(MaterialTheme.colorScheme.primaryContainer)
.padding(4.dp),
textStyle = Typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onSurface),
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
)
} }
Text(stringResource(R.string.in_x_days)) onExit()
} },
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
),
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Text(stringResource(R.string.remind_me_in_x))
BasicTextField(
value = remindDays.value,
onValueChange = {
remindDays.value = it
it.toFloatOrNull()
?.let { lastValidRemindValue.floatValue = it }
},
modifier = Modifier
.width(32.dp)
.border(Dp.Hairline, LocalContentColor.current)
.padding(4.dp),
textStyle = Typography.bodyMedium.copy(color = LocalContentColor.current),
cursorBrush = SolidColor(LocalContentColor.current),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
)
Text(stringResource(R.string.in_x_days))
} }
} }
} }
NavigationItem(title = "Help", subtitle = "Need help? Visit our website", style = NavigationItemStyle.Misc, navigate = {
context.openURI("https://keyboard.futo.org/")
})
} }
} }