improve whitespace handling in text classifier (#1696)

This commit is contained in:
Max Hübner
2026-01-25 10:28:22 +01:00
committed by GitHub
parent 8f9801b987
commit c6ea6dd4dc

View File

@@ -17,33 +17,34 @@ internal interface TextClassifier {
internal class TextClassifierImpl : TextClassifier { internal class TextClassifierImpl : TextClassifier {
override suspend fun classify(context: Context, query: String): TextClassificationResult { override suspend fun classify(context: Context, query: String): TextClassificationResult {
val trimmedQuery = query.trim()
return when { return when {
query.matches(Regex("^\\S+@\\S+$")) -> TextClassificationResult( trimmedQuery.matches(Regex("^\\S+@\\S+$")) -> TextClassificationResult(
type = TextType.Email, type = TextType.Email,
text = query, text = trimmedQuery,
email = query email = trimmedQuery
) )
query.matches(Regex("^\\+?[0-9- /.]{4,18}$")) -> TextClassificationResult( trimmedQuery.matches(Regex("^\\+?[0-9- /.]{4,18}$")) -> TextClassificationResult(
type = TextType.PhoneNumber, type = TextType.PhoneNumber,
text = query, text = trimmedQuery,
phoneNumber = query phoneNumber = trimmedQuery
) )
query.matches(Regex("^(http(s)?://.)?(www\\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$")) -> TextClassificationResult( trimmedQuery.matches(Regex("^(http(s)?://.)?(www\\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$")) -> TextClassificationResult(
type = TextType.Url, type = TextType.Url,
text = query, text = trimmedQuery,
url = query url = trimmedQuery
) )
else -> { else -> {
parseDate(context, query)?.let { return it } parseDate(context, trimmedQuery)?.let { return it }
TextClassificationResult(type = TextType.Text, text = query) TextClassificationResult(type = TextType.Text, text = trimmedQuery)
} }
} }
} }
private fun parseDate(context: Context, query: String): TextClassificationResult? { private fun parseDate(context: Context, trimmedQuery: String): TextClassificationResult? {
val dateTimeFormat = SimpleDateFormat( val dateTimeFormat = SimpleDateFormat(
DateFormat.getBestDateTimePattern( DateFormat.getBestDateTimePattern(
Locale.getDefault(), Locale.getDefault(),
@@ -52,11 +53,11 @@ internal class TextClassifierImpl : TextClassifier {
context.resources.configuration.locales[0] context.resources.configuration.locales[0]
) )
try { try {
dateTimeFormat.parse(query)?.let { dateTimeFormat.parse(trimmedQuery)?.let {
val dateTime = LocalDateTime.ofInstant(it.toInstant(), ZoneId.systemDefault()) val dateTime = LocalDateTime.ofInstant(it.toInstant(), ZoneId.systemDefault())
return TextClassificationResult( return TextClassificationResult(
type = TextType.DateTime, type = TextType.DateTime,
text = query, text = trimmedQuery,
time = dateTime.toLocalTime(), time = dateTime.toLocalTime(),
date = dateTime.toLocalDate(), date = dateTime.toLocalDate(),
) )
@@ -66,10 +67,10 @@ internal class TextClassifierImpl : TextClassifier {
} }
val dateFormat = DateFormat.getDateFormat(context) val dateFormat = DateFormat.getDateFormat(context)
try { try {
dateFormat.parse(query)?.let { dateFormat.parse(trimmedQuery)?.let {
return TextClassificationResult( return TextClassificationResult(
type = TextType.Date, type = TextType.Date,
text = query, text = trimmedQuery,
date = LocalDateTime.ofInstant(it.toInstant(), ZoneId.systemDefault()) date = LocalDateTime.ofInstant(it.toInstant(), ZoneId.systemDefault())
.toLocalDate() .toLocalDate()
) )
@@ -85,10 +86,10 @@ internal class TextClassifierImpl : TextClassifier {
context.resources.configuration.locales[0] context.resources.configuration.locales[0]
) )
try { try {
timeFormat.parse(query)?.let { timeFormat.parse(trimmedQuery)?.let {
return TextClassificationResult( return TextClassificationResult(
type = TextType.Time, type = TextType.Time,
text = query, text = trimmedQuery,
time = LocalDateTime.ofInstant(it.toInstant(), ZoneId.systemDefault()) time = LocalDateTime.ofInstant(it.toInstant(), ZoneId.systemDefault())
.toLocalTime(), .toLocalTime(),
) )
@@ -98,40 +99,46 @@ internal class TextClassifierImpl : TextClassifier {
} }
val seconds = context.getString(R.string.unit_second_symbol) val seconds = context.getString(R.string.unit_second_symbol)
if (query.matches(Regex("^[0-9]+ ${seconds}$"))) { val secondsMatch = Regex("^([0-9]+)\\s?${seconds}$").find(trimmedQuery)
val value = query.substringBefore(" ").toLong() if (secondsMatch != null) {
val value = secondsMatch.groups[1]!!.value.toLong()
return TextClassificationResult( return TextClassificationResult(
type = TextType.Timespan, type = TextType.Timespan,
text = query, text = trimmedQuery,
timespan = Duration.ofSeconds(value) timespan = Duration.ofSeconds(value)
) )
} }
val days = context.getString(R.string.unit_day_symbol) val days = context.getString(R.string.unit_day_symbol)
if (query.matches(Regex("^[0-9]+ ${days}$"))) { val daysMatch = Regex("^([0-9]+)\\s?${days}$").find(trimmedQuery)
val value = query.substringBefore(" ").toLong() if (daysMatch != null) {
val value = daysMatch.groups[1]!!.value.toLong()
return TextClassificationResult( return TextClassificationResult(
type = TextType.Timespan, type = TextType.Timespan,
text = query, text = trimmedQuery,
timespan = Duration.ofDays(value) timespan = Duration.ofDays(value)
) )
} }
val minutes = context.getString(R.string.unit_minute_symbol) val minutes = context.getString(R.string.unit_minute_symbol)
if (query.matches(Regex("^[0-9]+ ${minutes}$"))) { val minutesMatch = Regex("^([0-9]+)\\s?${minutes}$").find(trimmedQuery)
val value = query.substringBefore(" ").toLong() if (minutesMatch != null) {
val value = minutesMatch.groups[1]!!.value.toLong()
val then = LocalDateTime.now().plusMinutes(value) val then = LocalDateTime.now().plusMinutes(value)
return TextClassificationResult( return TextClassificationResult(
type = TextType.Timespan, type = TextType.Timespan,
text = query, text = trimmedQuery,
timespan = Duration.ofMinutes(value) timespan = Duration.ofMinutes(value)
) )
} }
val hours = context.getString(R.string.unit_hour_symbol) val hours = context.getString(R.string.unit_hour_symbol)
if (query.matches(Regex("^[0-9]+ ${hours}$"))) { val hoursMatch = Regex("^([0-9]+)\\s?${hours}$").find(trimmedQuery)
val value = query.substringBefore(" ").toLong() if (hoursMatch != null) {
val value = hoursMatch.groups[1]!!.value.toLong()
return TextClassificationResult( return TextClassificationResult(
type = TextType.Timespan, type = TextType.Timespan,
text = query, text = trimmedQuery,
timespan = Duration.ofHours(value) timespan = Duration.ofHours(value)
) )
} }