diff --git a/android/app/src/main/java/org/bitcoinppl/cove/flows/SendFlow/EnterAmountView.kt b/android/app/src/main/java/org/bitcoinppl/cove/flows/SendFlow/EnterAmountView.kt index cf6f504b1..b9dbe2675 100644 --- a/android/app/src/main/java/org/bitcoinppl/cove/flows/SendFlow/EnterAmountView.kt +++ b/android/app/src/main/java/org/bitcoinppl/cove/flows/SendFlow/EnterAmountView.kt @@ -17,6 +17,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -29,8 +30,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -62,6 +65,13 @@ fun EnterAmountView( var textWidth by remember { mutableStateOf(0.dp) } var isFocused by remember { mutableStateOf(false) } + // fall back to a local FocusRequester so the clickable wrapper can always + // programmatically focus the TextField even when the parent doesn't pass one + val localFocusRequester = remember { FocusRequester() } + val effectiveFocusRequester = focusRequester ?: localFocusRequester + + val showTapPlaceholder = !isFocused && (amount.isEmpty() || amount == "0") + // offset to compensate for unit dropdown (matches iOS) val configuration = LocalConfiguration.current val screenWidthDp = configuration.screenWidthDp.dp @@ -98,7 +108,16 @@ fun EnterAmountView( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.Bottom, ) { - Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.Center) { + Box( + modifier = + Modifier + .weight(1f) + // extend the tap target beyond the text glyphs and provide + // a ripple; BasicTextField still consumes taps within its + // own bounds, so this only fires for taps on empty space + .clickable { effectiveFocusRequester.requestFocus() }, + contentAlignment = Alignment.Center, + ) { AutoSizeTextField( value = amount, onValueChange = { newValue -> @@ -118,18 +137,44 @@ fun EnterAmountView( }, maxFontSize = 48.sp, minimumScaleFactor = 0.01f, - color = if (exceedsBalance) CoveColor.WarningOrange else MaterialTheme.colorScheme.onSurface, + color = + when { + // hide the default "0" value behind the placeholder so + // we don't see both at once + showTapPlaceholder -> Color.Transparent + exceedsBalance -> CoveColor.WarningOrange + else -> MaterialTheme.colorScheme.onSurface + }, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth().padding(horizontal = 30.dp).offset(x = amountOffset), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 30.dp) + .offset(x = amountOffset) + // remove from accessibility tree when hidden behind + // the placeholder so TalkBack doesn't announce both + .then( + if (showTapPlaceholder) Modifier.clearAndSetSemantics {} else Modifier, + ), onTextWidthChanged = { width -> textWidth = width }, onFocusChanged = { focused -> isFocused = focused onFocusChanged(focused) }, keyboardActions = KeyboardActions(onDone = { onDone() }), - focusRequester = focusRequester, + focusRequester = effectiveFocusRequester, ) + + if (showTapPlaceholder) { + Text( + text = stringResource(R.string.placeholder_tap_to_enter_amount), + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + modifier = Modifier.offset(x = amountOffset), + ) + } } // unit dropdown area (only shown when in BTC mode, matches iOS) if (!isFiatMode) { @@ -174,6 +219,11 @@ fun EnterAmountView( } } Spacer(Modifier.height(8.dp)) + HorizontalDivider( + color = MaterialTheme.colorScheme.outlineVariant, + thickness = 1.dp, + ) + Spacer(Modifier.height(8.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center, diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index ec77961ea..3f522c097 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -98,6 +98,7 @@ Balance Enter amount How much would you like to send? + Tap to enter amount Enter address Where do you want to send to? Account