fix: ensure unique call log items to prevent crashes (#534)

* fix: ensure unique call log items to prevent crashes

Refs: https://github.com/FossifyOrg/Phone/issues/378

* fix: prevent incorrect sorting in search results

Changed the day code format (used internally) to yyyy-MM-dd to help with sorting. Corrected sorting also solves duplicated date headers due to misplaced call entries (caused by `startsWith()` sort).

* perf(recents): cache call day code to eliminate allocations

* fix: simplify condition

* refactor: no need to compute day code lazily
This commit is contained in:
Naveen Singh
2025-08-23 21:13:11 +05:30
committed by GitHub
parent d9713a3e7a
commit 47b9a83c99
6 changed files with 47 additions and 23 deletions

View File

@@ -5,9 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Changed
- Renamed notification channels to be more user-friendly ([#196])
### Fixed
- Fixed incorrect sorting in call history search results
- Fixed missing phone number in call history details ([#526])
- Renamed notification channels ([#196])
- Fixed frequent crashes in call history ([#378])
## [1.6.1] - 2025-07-31
### Changed

View File

@@ -602,9 +602,13 @@ class RecentCallsAdapter(
val now = DateTime.now()
text = when (date.dayCode) {
now.millis.toDayCode() -> activity.getString(R.string.today)
now.minusDays(1).millis.toDayCode() -> activity.getString(R.string.yesterday)
else -> date.timestamp.formatDateOrTime(activity, hideTimeOnOtherDays = true, showCurrentYear = false)
now.millis.getDayCode() -> activity.getString(R.string.today)
now.minusDays(1).millis.getDayCode() -> activity.getString(R.string.yesterday)
else -> date.timestamp.formatDateOrTime(
context = activity,
hideTimeOnOtherDays = true,
showCurrentYear = false
)
}
}
}

View File

@@ -0,0 +1,7 @@
package org.fossify.phone.extensions
import org.fossify.commons.extensions.toDayCode
fun Long.getDayCode(): String {
return toDayCode("yyyy-MM-dd") // format helps with sorting in call log
}

View File

@@ -88,11 +88,15 @@ class RecentsFragment(
ensureBackgroundThread {
val fixedText = searchQuery!!.trim().replace("\\s+".toRegex(), " ")
val recentCalls = allRecentCalls
.filterIsInstance<RecentCall>()
.filter {
it is RecentCall && (it.name.contains(fixedText, true) || it.doesContainPhoneNumber(fixedText))
}.sortedByDescending {
it is RecentCall && it.name.startsWith(fixedText, true)
} as List<RecentCall>
it.name.contains(fixedText, true) || it.doesContainPhoneNumber(fixedText)
}
.sortedWith(
compareByDescending<RecentCall> { it.dayCode }
.thenByDescending { it.name.startsWith(fixedText, true) }
.thenByDescending { it.startTS }
)
prepareCallLog(recentCalls) {
activity?.runOnUiThread {
@@ -275,7 +279,7 @@ class RecentsFragment(
val callLog = mutableListOf<CallLogItem>()
var lastDayCode = ""
for (call in recentCalls) {
val currentDayCode = call.getDayCode()
val currentDayCode = call.dayCode
if (currentDayCode != lastDayCode) {
callLog += CallLogItem.Date(timestamp = call.startTS, dayCode = currentDayCode)
lastDayCode = currentDayCode

View File

@@ -43,9 +43,9 @@ class RecentsHelper(private val context: Context) {
this.queryLimit = queryLimit
val recentCalls = if (previousRecents.isNotEmpty()) {
val previousRecentCalls = previousRecents.flatMap {
it.groupedCalls ?: listOf(it)
}
val previousRecentCalls = previousRecents
.flatMap { it.groupedCalls ?: listOf(it) }
.map { it.copy(groupedCalls = null) }
val newerRecents = getRecents(
contacts = contacts,
@@ -64,7 +64,11 @@ class RecentsHelper(private val context: Context) {
getRecents(contacts)
}
callback(recentCalls)
callback(
recentCalls
.sortedByDescending { it.startTS }
.distinctBy { it.id }
)
}
}
}
@@ -82,13 +86,14 @@ class RecentsHelper(private val context: Context) {
}
private fun shouldGroupCalls(callA: RecentCall, callB: RecentCall): Boolean {
if (
callA.simID != callB.simID
|| (callA.name != callB.name && callA.name != callA.phoneNumber && callB.name != callB.phoneNumber)
|| callA.getDayCode() != callB.getDayCode()
) {
return false
}
val differentSim = callA.simID != callB.simID
val differentDay = callA.dayCode != callB.dayCode
val namesAreBothRealAndDifferent =
callA.name != callB.name &&
callA.name != callA.phoneNumber &&
callB.name != callB.phoneNumber
if (differentSim || differentDay || namesAreBothRealAndDifferent) return false
@Suppress("DEPRECATION")
return PhoneNumberUtils.compare(callA.phoneNumber, callB.phoneNumber)

View File

@@ -2,7 +2,7 @@ package org.fossify.phone.models
import android.telephony.PhoneNumberUtils
import org.fossify.commons.extensions.normalizePhoneNumber
import org.fossify.commons.extensions.toDayCode
import org.fossify.phone.extensions.getDayCode
/**
* Used at displaying recent calls.
@@ -24,6 +24,8 @@ data class RecentCall(
val isUnknownNumber: Boolean,
val groupedCalls: MutableList<RecentCall>? = null,
) : CallLogItem() {
val dayCode = startTS.getDayCode()
fun doesContainPhoneNumber(text: String): Boolean {
return if (text.toIntOrNull() != null) {
val normalizedText = text.normalizePhoneNumber()
@@ -35,6 +37,4 @@ data class RecentCall(
false
}
}
fun getDayCode() = startTS.toDayCode()
}