Improved showing of suggestions on MainScreen

This commit is contained in:
Juha Heinanen
2026-01-18 22:44:45 +02:00
parent f28dbefbd9
commit c5bd9bdd80
3 changed files with 77 additions and 47 deletions

View File

@ -255,7 +255,7 @@ private fun ContactsContent(
Utils.unaccent(contact.name()).contains(normalizedQuery, ignoreCase = true)
}
.map { contact ->
Pair(contact, buildAnnotatedStringWithHighlight(contact.name(), searchQuery))
Pair(contact, Utils.buildAnnotatedStringWithHighlight(contact.name(), searchQuery))
}
}
}
@ -432,20 +432,3 @@ private fun ContactsContent(
}
}
}
private fun buildAnnotatedStringWithHighlight(name: String, query: String): AnnotatedString {
val normalizedName = Utils.unaccent(name)
val normalizedQuery = Utils.unaccent(query)
val startIndex = normalizedName.indexOf(normalizedQuery, ignoreCase = true)
return if (startIndex == -1) {
buildAnnotatedString { append(name) }
} else {
buildAnnotatedString {
append(name.take(startIndex))
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append(name.drop(startIndex).take(normalizedQuery.length))
}
append(name.drop(startIndex + normalizedQuery.length))
}
}
}

View File

@ -128,6 +128,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
@ -483,7 +484,9 @@ private fun MainScreen(
}
Scaffold(
modifier = Modifier.fillMaxSize().imePadding(),
modifier = Modifier
.fillMaxSize()
.imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(
@ -652,8 +655,7 @@ private fun TopAppBar(
onClick = {
if (Build.VERSION.SDK_INT >= 31)
Log.d(TAG, "Toggling speakerphone when dev/mode is " +
"${am.communicationDevice!!.type}/${am.mode}"
)
"${am.communicationDevice!!.type}/${am.mode}")
isSpeakerOn.value = !Utils.isSpeakerPhoneOn(am)
Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(ctx), am)
},
@ -826,7 +828,9 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
IconButton(
onClick = { viewModel.toggleDialpadVisibility() },
modifier = Modifier.weight(1f).size(buttonSize),
modifier = Modifier
.weight(1f)
.size(buttonSize),
enabled = dialpadButtonEnabled.value
) {
Icon(
@ -1060,8 +1064,7 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na
if (Api.account_regint(acc.accp) > 0) {
Api.account_set_regint(acc.accp, 0)
Api.ua_unregister(ua.uap)
}
else {
} else {
Api.account_set_regint(
acc.accp,
acc.configuredRegInt
@ -1112,8 +1115,7 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na
if (Api.account_regint(acc.accp) > 0) {
Api.account_set_regint(acc.accp, 0)
Api.ua_unregister(ua.uap)
}
else {
} else {
Api.account_set_regint(
acc.accp,
acc.configuredRegInt
@ -1179,15 +1181,17 @@ private fun CallUriRow(
val isDialer = dialerState != null
val suggestions by remember { contactNames }
var filteredSuggestions by remember { mutableStateOf(suggestions) }
var filteredSuggestions by remember { mutableStateOf<List<AnnotatedString>>(emptyList()) }
val focusRequester = remember { FocusRequester() }
val lazyListState = rememberLazyListState()
val isDialpadVisible by viewModel.isDialpadVisible.collectAsState()
Row(modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp, bottom = 8.dp),
verticalAlignment = Alignment.CenterVertically) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp, bottom = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally
@ -1200,10 +1204,21 @@ private fun CallUriRow(
if (isDialer) {
if (it != dialerState.callUri.value) {
dialerState.callUri.value = it
filteredSuggestions = suggestions.filter { suggestion ->
it.length > 2 && suggestion.startsWith(it, ignoreCase = true)
if (it == "") {
dialerState.showCallButton.value = true
dialerState.showCallConferenceButton.value = true
}
dialerState.showSuggestions.value = it.length > 2
val normalizedInput = Utils.unaccent(it)
filteredSuggestions = suggestions
.filter { suggestion ->
it.length > 1 &&
Utils.unaccent(suggestion)
.contains(normalizedInput, ignoreCase = true)
}
.map { suggestion ->
Utils.buildAnnotatedStringWithHighlight(suggestion, it)
}
dialerState.showSuggestions.value = it.length > 1
}
}
},
@ -1214,8 +1229,9 @@ private fun CallUriRow(
modifier = Modifier.clickable {
if (dialerState.showSuggestions.value)
dialerState.showSuggestions.value = false
else
dialerState.callUri.value = ""
dialerState.callUri.value = ""
dialerState.showCallButton.value = true
dialerState.showCallConferenceButton.value = true
},
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
@ -1233,10 +1249,11 @@ private fun CallUriRow(
}
},
label = {
Text(text = if (isDialer)
dialerState.callUriLabel.value
else
call!!.callUriLabel.value,
Text(
text = if (isDialer)
dialerState.callUriLabel.value
else
call!!.callUriLabel.value,
fontSize = 18.sp
)
},
@ -1258,9 +1275,11 @@ private fun CallUriRow(
.animateContentSize()
) {
if (isDialer && dialerState.showSuggestions.value && filteredSuggestions.isNotEmpty()) {
Box(modifier = Modifier
.fillMaxWidth()
.heightIn(max = 150.dp)) {
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(max = 150.dp)
) {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
@ -1273,13 +1292,13 @@ private fun CallUriRow(
) {
items(
items = filteredSuggestions,
key = { suggestion -> suggestion }
key = { suggestion -> suggestion.toString() }
) { suggestion ->
Box(
modifier = Modifier
.fillMaxWidth()
.clickable {
dialerState.callUri.value = suggestion
dialerState.callUri.value = suggestion.toString()
dialerState.showSuggestions.value = false
}
.padding(12.dp)
@ -1619,7 +1638,12 @@ private fun CallRow(
},
modifier = Modifier
.fillMaxWidth()
.padding(start = 4.dp, end = 4.dp, top = 12.dp, bottom = 2.dp)
.padding(
start = 4.dp,
end = 4.dp,
top = 12.dp,
bottom = 2.dp
)
.focusRequester(focusRequester),
label = { Text(stringResource(R.string.transfer_destination)) },
textStyle = TextStyle(fontSize = 18.sp),
@ -1662,7 +1686,8 @@ private fun CallRow(
.fillMaxWidth()
.clickable {
transferUri = suggestion
call.showSuggestions.value = false
call.showSuggestions.value =
false
}
.padding(12.dp)
) {

View File

@ -29,6 +29,11 @@ import android.text.format.DateUtils
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.core.content.ContextCompat
import androidx.core.graphics.toColorInt
import androidx.core.net.toUri
@ -424,6 +429,23 @@ object Utils {
return "\\p{InCombiningDiacriticalMarks}+".toRegex().replace(normalized, "")
}
fun buildAnnotatedStringWithHighlight(name: String, query: String): AnnotatedString {
val normalizedName = Utils.unaccent(name)
val normalizedQuery = Utils.unaccent(query)
val startIndex = normalizedName.indexOf(normalizedQuery, ignoreCase = true)
return if (startIndex == -1) {
buildAnnotatedString { append(name) }
} else {
buildAnnotatedString {
append(name.take(startIndex))
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append(name.drop(startIndex).take(normalizedQuery.length))
}
append(name.drop(startIndex + normalizedQuery.length))
}
}
}
fun isVisible(): Boolean {
return ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
}