Improved showing of suggestions on MainScreen
This commit is contained in:
@ -255,7 +255,7 @@ private fun ContactsContent(
|
|||||||
Utils.unaccent(contact.name()).contains(normalizedQuery, ignoreCase = true)
|
Utils.unaccent(contact.name()).contains(normalizedQuery, ignoreCase = true)
|
||||||
}
|
}
|
||||||
.map { contact ->
|
.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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -128,6 +128,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
|||||||
import androidx.compose.ui.res.colorResource
|
import androidx.compose.ui.res.colorResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
@ -483,7 +484,9 @@ private fun MainScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize().imePadding(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.imePadding(),
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
Column(
|
Column(
|
||||||
@ -652,8 +655,7 @@ private fun TopAppBar(
|
|||||||
onClick = {
|
onClick = {
|
||||||
if (Build.VERSION.SDK_INT >= 31)
|
if (Build.VERSION.SDK_INT >= 31)
|
||||||
Log.d(TAG, "Toggling speakerphone when dev/mode is " +
|
Log.d(TAG, "Toggling speakerphone when dev/mode is " +
|
||||||
"${am.communicationDevice!!.type}/${am.mode}"
|
"${am.communicationDevice!!.type}/${am.mode}")
|
||||||
)
|
|
||||||
isSpeakerOn.value = !Utils.isSpeakerPhoneOn(am)
|
isSpeakerOn.value = !Utils.isSpeakerPhoneOn(am)
|
||||||
Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(ctx), am)
|
Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(ctx), am)
|
||||||
},
|
},
|
||||||
@ -826,7 +828,9 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { viewModel.toggleDialpadVisibility() },
|
onClick = { viewModel.toggleDialpadVisibility() },
|
||||||
modifier = Modifier.weight(1f).size(buttonSize),
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.size(buttonSize),
|
||||||
enabled = dialpadButtonEnabled.value
|
enabled = dialpadButtonEnabled.value
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@ -1060,8 +1064,7 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na
|
|||||||
if (Api.account_regint(acc.accp) > 0) {
|
if (Api.account_regint(acc.accp) > 0) {
|
||||||
Api.account_set_regint(acc.accp, 0)
|
Api.account_set_regint(acc.accp, 0)
|
||||||
Api.ua_unregister(ua.uap)
|
Api.ua_unregister(ua.uap)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Api.account_set_regint(
|
Api.account_set_regint(
|
||||||
acc.accp,
|
acc.accp,
|
||||||
acc.configuredRegInt
|
acc.configuredRegInt
|
||||||
@ -1112,8 +1115,7 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na
|
|||||||
if (Api.account_regint(acc.accp) > 0) {
|
if (Api.account_regint(acc.accp) > 0) {
|
||||||
Api.account_set_regint(acc.accp, 0)
|
Api.account_set_regint(acc.accp, 0)
|
||||||
Api.ua_unregister(ua.uap)
|
Api.ua_unregister(ua.uap)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Api.account_set_regint(
|
Api.account_set_regint(
|
||||||
acc.accp,
|
acc.accp,
|
||||||
acc.configuredRegInt
|
acc.configuredRegInt
|
||||||
@ -1179,15 +1181,17 @@ private fun CallUriRow(
|
|||||||
val isDialer = dialerState != null
|
val isDialer = dialerState != null
|
||||||
|
|
||||||
val suggestions by remember { contactNames }
|
val suggestions by remember { contactNames }
|
||||||
var filteredSuggestions by remember { mutableStateOf(suggestions) }
|
var filteredSuggestions by remember { mutableStateOf<List<AnnotatedString>>(emptyList()) }
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
val isDialpadVisible by viewModel.isDialpadVisible.collectAsState()
|
val isDialpadVisible by viewModel.isDialpadVisible.collectAsState()
|
||||||
|
|
||||||
Row(modifier = Modifier
|
Row(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(top = 4.dp, bottom = 8.dp),
|
.fillMaxWidth()
|
||||||
verticalAlignment = Alignment.CenterVertically) {
|
.padding(top = 4.dp, bottom = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
@ -1200,10 +1204,21 @@ private fun CallUriRow(
|
|||||||
if (isDialer) {
|
if (isDialer) {
|
||||||
if (it != dialerState.callUri.value) {
|
if (it != dialerState.callUri.value) {
|
||||||
dialerState.callUri.value = it
|
dialerState.callUri.value = it
|
||||||
filteredSuggestions = suggestions.filter { suggestion ->
|
if (it == "") {
|
||||||
it.length > 2 && suggestion.startsWith(it, ignoreCase = true)
|
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 {
|
modifier = Modifier.clickable {
|
||||||
if (dialerState.showSuggestions.value)
|
if (dialerState.showSuggestions.value)
|
||||||
dialerState.showSuggestions.value = false
|
dialerState.showSuggestions.value = false
|
||||||
else
|
dialerState.callUri.value = ""
|
||||||
dialerState.callUri.value = ""
|
dialerState.showCallButton.value = true
|
||||||
|
dialerState.showCallConferenceButton.value = true
|
||||||
},
|
},
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
@ -1233,10 +1249,11 @@ private fun CallUriRow(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
label = {
|
label = {
|
||||||
Text(text = if (isDialer)
|
Text(
|
||||||
dialerState.callUriLabel.value
|
text = if (isDialer)
|
||||||
else
|
dialerState.callUriLabel.value
|
||||||
call!!.callUriLabel.value,
|
else
|
||||||
|
call!!.callUriLabel.value,
|
||||||
fontSize = 18.sp
|
fontSize = 18.sp
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -1258,9 +1275,11 @@ private fun CallUriRow(
|
|||||||
.animateContentSize()
|
.animateContentSize()
|
||||||
) {
|
) {
|
||||||
if (isDialer && dialerState.showSuggestions.value && filteredSuggestions.isNotEmpty()) {
|
if (isDialer && dialerState.showSuggestions.value && filteredSuggestions.isNotEmpty()) {
|
||||||
Box(modifier = Modifier
|
Box(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.heightIn(max = 150.dp)) {
|
.fillMaxWidth()
|
||||||
|
.heightIn(max = 150.dp)
|
||||||
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -1273,13 +1292,13 @@ private fun CallUriRow(
|
|||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = filteredSuggestions,
|
items = filteredSuggestions,
|
||||||
key = { suggestion -> suggestion }
|
key = { suggestion -> suggestion.toString() }
|
||||||
) { suggestion ->
|
) { suggestion ->
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
dialerState.callUri.value = suggestion
|
dialerState.callUri.value = suggestion.toString()
|
||||||
dialerState.showSuggestions.value = false
|
dialerState.showSuggestions.value = false
|
||||||
}
|
}
|
||||||
.padding(12.dp)
|
.padding(12.dp)
|
||||||
@ -1619,7 +1638,12 @@ private fun CallRow(
|
|||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.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),
|
.focusRequester(focusRequester),
|
||||||
label = { Text(stringResource(R.string.transfer_destination)) },
|
label = { Text(stringResource(R.string.transfer_destination)) },
|
||||||
textStyle = TextStyle(fontSize = 18.sp),
|
textStyle = TextStyle(fontSize = 18.sp),
|
||||||
@ -1662,7 +1686,8 @@ private fun CallRow(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
transferUri = suggestion
|
transferUri = suggestion
|
||||||
call.showSuggestions.value = false
|
call.showSuggestions.value =
|
||||||
|
false
|
||||||
}
|
}
|
||||||
.padding(12.dp)
|
.padding(12.dp)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -29,6 +29,11 @@ import android.text.format.DateUtils
|
|||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
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.content.ContextCompat
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
@ -424,6 +429,23 @@ object Utils {
|
|||||||
return "\\p{InCombiningDiacriticalMarks}+".toRegex().replace(normalized, "")
|
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 {
|
fun isVisible(): Boolean {
|
||||||
return ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
|
return ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user