mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 09:49:30 +01:00
Improve call tab performance.
This commit is contained in:
committed by
Greyson Parrelli
parent
71c21eeba6
commit
0b24e42448
@@ -13,7 +13,6 @@ import org.signal.core.util.deleteAll
|
||||
import org.signal.core.util.exists
|
||||
import org.signal.core.util.flatten
|
||||
import org.signal.core.util.insertInto
|
||||
import org.signal.core.util.isAbsent
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.readToList
|
||||
import org.signal.core.util.readToMap
|
||||
@@ -23,7 +22,6 @@ import org.signal.core.util.requireBoolean
|
||||
import org.signal.core.util.requireLong
|
||||
import org.signal.core.util.requireNonNullString
|
||||
import org.signal.core.util.requireObject
|
||||
import org.signal.core.util.requireString
|
||||
import org.signal.core.util.select
|
||||
import org.signal.core.util.toInt
|
||||
import org.signal.core.util.toSingleLine
|
||||
@@ -31,9 +29,6 @@ import org.signal.core.util.update
|
||||
import org.signal.core.util.withinTransaction
|
||||
import org.signal.ringrtc.CallId
|
||||
import org.signal.ringrtc.CallManager.RingUpdate
|
||||
import org.thoughtcrime.securesms.calls.log.CallLogFilter
|
||||
import org.thoughtcrime.securesms.calls.log.CallLogRow
|
||||
import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.CallLinkUpdateSendJob
|
||||
@@ -238,7 +233,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
|
||||
.readToSingleObject(Call.Deserializer)
|
||||
}
|
||||
|
||||
fun getCalls(messageIds: Collection<Long>): Map<Long, Call> {
|
||||
fun getCallsForCache(messageIds: Collection<Long>): Map<Long, Call> {
|
||||
val queries = SqlUtil.buildCollectionQuery(MESSAGE_ID, messageIds)
|
||||
val maps = queries.map { query ->
|
||||
readableDatabase
|
||||
@@ -1242,180 +1237,6 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
|
||||
|
||||
// endregion
|
||||
|
||||
private fun getCallsCursor(isCount: Boolean, offset: Int, limit: Int, searchTerm: String?, filter: CallLogFilter): Cursor {
|
||||
val isMissedGenericGroupCall = "$EVENT = ${Event.serialize(Event.GENERIC_GROUP_CALL)} AND $LOCAL_JOINED = ${false.toInt()} AND $GROUP_CALL_ACTIVE = ${false.toInt()}"
|
||||
val filterClause: SqlUtil.Query = when (filter) {
|
||||
CallLogFilter.ALL -> SqlUtil.buildQuery("$DELETION_TIMESTAMP = 0")
|
||||
CallLogFilter.MISSED -> SqlUtil.buildQuery("$TYPE != ${Type.serialize(Type.AD_HOC_CALL)} AND $DIRECTION == ${Direction.serialize(Direction.INCOMING)} AND ($EVENT = ${Event.serialize(Event.MISSED)} OR $EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} OR $EVENT = ${Event.serialize(Event.NOT_ACCEPTED)} OR $EVENT = ${Event.serialize(Event.DECLINED)} OR ($isMissedGenericGroupCall)) AND $DELETION_TIMESTAMP = 0")
|
||||
CallLogFilter.AD_HOC -> SqlUtil.buildQuery("$TYPE = ${Type.serialize(Type.AD_HOC_CALL)} AND $DELETION_TIMESTAMP = 0")
|
||||
}
|
||||
|
||||
val queryClause: SqlUtil.Query = if (!searchTerm.isNullOrEmpty()) {
|
||||
val glob = SqlUtil.buildCaseInsensitiveGlobPattern(searchTerm)
|
||||
val selection =
|
||||
"""
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED} = ? AND ${RecipientTable.TABLE_NAME}.${RecipientTable.HIDDEN} = ? AND
|
||||
(
|
||||
sort_name GLOB ? OR
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.USERNAME} GLOB ? OR
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.E164} GLOB ? OR
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.EMAIL} GLOB ?
|
||||
)
|
||||
"""
|
||||
SqlUtil.buildQuery(selection, 0, 0, glob, glob, glob, glob)
|
||||
} else {
|
||||
SqlUtil.buildQuery(
|
||||
"""
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED} = ? AND ${RecipientTable.TABLE_NAME}.${RecipientTable.HIDDEN} = ?
|
||||
""",
|
||||
0,
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
val offsetLimit = if (limit > 0) {
|
||||
"LIMIT $offset,$limit"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
val projection = if (isCount) {
|
||||
"COUNT(*) OVER() as count"
|
||||
} else {
|
||||
"p.$ID, p.$TIMESTAMP, $EVENT, $DIRECTION, $PEER, p.$TYPE, $CALL_ID, $MESSAGE_ID, $RINGER, $LOCAL_JOINED, $GROUP_CALL_ACTIVE, children, in_period, ${MessageTable.BODY}"
|
||||
}
|
||||
|
||||
val recipientSearchProjection = if (searchTerm.isNullOrEmpty()) {
|
||||
""
|
||||
} else {
|
||||
"""
|
||||
,LOWER(
|
||||
COALESCE(
|
||||
NULLIF(${GroupTable.TABLE_NAME}.${GroupTable.TITLE}, ''),
|
||||
NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.NICKNAME_JOINED_NAME}, ''),
|
||||
NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.NICKNAME_GIVEN_NAME}, ''),
|
||||
NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.SYSTEM_JOINED_NAME}, ''),
|
||||
NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.SYSTEM_GIVEN_NAME}, ''),
|
||||
NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_JOINED_NAME}, ''),
|
||||
NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_GIVEN_NAME}, ''),
|
||||
NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.USERNAME}, '')
|
||||
)
|
||||
) AS sort_name
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
val join = if (isCount) {
|
||||
""
|
||||
} else {
|
||||
"LEFT JOIN ${MessageTable.TABLE_NAME} ON ${MessageTable.TABLE_NAME}.${MessageTable.ID} = $MESSAGE_ID"
|
||||
}
|
||||
|
||||
// Group call events by those we consider missed or not missed to build out our call log aggregation.
|
||||
val eventTypeSubQuery = """
|
||||
($TABLE_NAME.$EVENT = c.$EVENT AND (
|
||||
$TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED)} OR
|
||||
$TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} OR
|
||||
$TABLE_NAME.$EVENT = ${Event.serialize(Event.NOT_ACCEPTED)} OR
|
||||
$TABLE_NAME.$EVENT = ${Event.serialize(Event.DECLINED)} OR
|
||||
($TABLE_NAME.$isMissedGenericGroupCall)
|
||||
)) OR (
|
||||
$TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED)} AND
|
||||
c.$EVENT != ${Event.serialize(Event.MISSED)} AND
|
||||
$TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} AND
|
||||
c.$EVENT != ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} AND
|
||||
$TABLE_NAME.$EVENT != ${Event.serialize(Event.NOT_ACCEPTED)} AND
|
||||
c.$EVENT != ${Event.serialize(Event.NOT_ACCEPTED)} AND
|
||||
$TABLE_NAME.$EVENT != ${Event.serialize(Event.DECLINED)} AND
|
||||
c.$EVENT != ${Event.serialize(Event.DECLINED)} AND
|
||||
(NOT ($TABLE_NAME.$isMissedGenericGroupCall)) AND
|
||||
(NOT (c.$isMissedGenericGroupCall))
|
||||
)
|
||||
"""
|
||||
|
||||
//language=sql
|
||||
val statement = """
|
||||
SELECT $projection
|
||||
$recipientSearchProjection
|
||||
FROM (
|
||||
WITH cte AS (
|
||||
SELECT
|
||||
$ID, $TIMESTAMP, $EVENT, $DIRECTION, $PEER, $TYPE, $CALL_ID, $MESSAGE_ID, $RINGER, $LOCAL_JOINED, $GROUP_CALL_ACTIVE,
|
||||
(
|
||||
SELECT
|
||||
$ID
|
||||
FROM
|
||||
$TABLE_NAME
|
||||
WHERE
|
||||
$TABLE_NAME.$DIRECTION = c.$DIRECTION
|
||||
AND $TABLE_NAME.$PEER = c.$PEER
|
||||
AND $TABLE_NAME.$TIMESTAMP - $TIME_WINDOW <= c.$TIMESTAMP
|
||||
AND $TABLE_NAME.$TIMESTAMP >= c.$TIMESTAMP
|
||||
AND ($eventTypeSubQuery)
|
||||
AND ${filterClause.where}
|
||||
ORDER BY
|
||||
$TIMESTAMP DESC
|
||||
) as parent,
|
||||
(
|
||||
SELECT
|
||||
group_concat($ID)
|
||||
FROM
|
||||
$TABLE_NAME
|
||||
WHERE
|
||||
$TABLE_NAME.$DIRECTION = c.$DIRECTION
|
||||
AND $TABLE_NAME.$PEER = c.$PEER
|
||||
AND c.$TIMESTAMP - $TIME_WINDOW <= $TABLE_NAME.$TIMESTAMP
|
||||
AND c.$TIMESTAMP >= $TABLE_NAME.$TIMESTAMP
|
||||
AND ($eventTypeSubQuery)
|
||||
AND ${filterClause.where}
|
||||
) as children,
|
||||
(
|
||||
SELECT
|
||||
group_concat($ID)
|
||||
FROM
|
||||
$TABLE_NAME
|
||||
WHERE
|
||||
c.$TIMESTAMP - $TIME_WINDOW <= $TABLE_NAME.$TIMESTAMP
|
||||
AND c.$TIMESTAMP >= $TABLE_NAME.$TIMESTAMP
|
||||
AND ${filterClause.where}
|
||||
) as in_period
|
||||
FROM
|
||||
$TABLE_NAME c INDEXED BY $CALL_LOG_INDEX
|
||||
WHERE ${filterClause.where}
|
||||
ORDER BY
|
||||
$TIMESTAMP DESC
|
||||
)
|
||||
SELECT
|
||||
*,
|
||||
CASE
|
||||
WHEN LAG (parent, 1, 0) OVER (
|
||||
ORDER BY
|
||||
$TIMESTAMP DESC
|
||||
) != parent THEN $ID
|
||||
ELSE parent
|
||||
END true_parent
|
||||
FROM
|
||||
cte
|
||||
) p
|
||||
INNER JOIN ${RecipientTable.TABLE_NAME} ON ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} = $PEER
|
||||
$join
|
||||
LEFT JOIN ${GroupTable.TABLE_NAME} ON ${GroupTable.TABLE_NAME}.${GroupTable.RECIPIENT_ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID}
|
||||
WHERE true_parent = p.$ID
|
||||
AND CASE
|
||||
WHEN p.$TYPE = ${Type.serialize(Type.AD_HOC_CALL)} THEN EXISTS (SELECT * FROM ${CallLinkTable.TABLE_NAME} WHERE ${CallLinkTable.RECIPIENT_ID} = $PEER AND ${CallLinkTable.ROOT_KEY} NOT NULL)
|
||||
ELSE 1
|
||||
END
|
||||
${if (queryClause.where.isNotEmpty()) "AND ${queryClause.where}" else ""}
|
||||
GROUP BY CASE WHEN p.type = 4 THEN p.peer ELSE p._id END
|
||||
ORDER BY p.$TIMESTAMP DESC
|
||||
$offsetLimit
|
||||
"""
|
||||
|
||||
return readableDatabase.query(
|
||||
statement,
|
||||
queryClause.whereArgs
|
||||
)
|
||||
}
|
||||
|
||||
fun getLatestRingingCalls(): List<Call> {
|
||||
return readableDatabase.select()
|
||||
.from(TABLE_NAME)
|
||||
@@ -1445,56 +1266,20 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
|
||||
}
|
||||
}
|
||||
|
||||
fun getCallsCount(searchTerm: String?, filter: CallLogFilter): Int {
|
||||
return getCallsCursor(true, 0, 1, searchTerm, filter).use {
|
||||
if (it.moveToFirst()) {
|
||||
it.getInt(0)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCalls(offset: Int, limit: Int, searchTerm: String?, filter: CallLogFilter): List<CallLogRow.Call> {
|
||||
return getCallsCursor(false, offset, limit, searchTerm, filter).readToList { cursor ->
|
||||
val call = Call.deserialize(cursor)
|
||||
val groupCallDetails = GroupCallUpdateDetailsUtil.parse(cursor.requireString(MessageTable.BODY))
|
||||
|
||||
val children = cursor.requireNonNullString("children")
|
||||
.split(',')
|
||||
.map { it.toLong() }
|
||||
.toSet()
|
||||
|
||||
val inPeriod = cursor.requireNonNullString("in_period")
|
||||
.split(',')
|
||||
.map { it.toLong() }
|
||||
.sortedDescending()
|
||||
.toSet()
|
||||
|
||||
val actualChildren = inPeriod.takeWhile { children.contains(it) }
|
||||
val peer = Recipient.resolved(call.peer)
|
||||
|
||||
val canUserBeginCall = if (peer.isGroup) {
|
||||
val record = SignalDatabase.groups.getGroup(peer.id)
|
||||
|
||||
!record.isAbsent() &&
|
||||
record.get().isActive &&
|
||||
(!record.get().isAnnouncementGroup || record.get().memberLevel(Recipient.self()) == GroupTable.MemberLevel.ADMINISTRATOR)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
CallLogRow.Call(
|
||||
record = call,
|
||||
date = call.timestamp,
|
||||
peer = peer,
|
||||
groupCallState = CallLogRow.GroupCallState.fromDetails(groupCallDetails),
|
||||
children = actualChildren.toSet(),
|
||||
searchQuery = searchTerm,
|
||||
callLinkPeekInfo = AppDependencies.signalCallManager.peekInfoSnapshot[peer.id],
|
||||
canUserBeginCall = canUserBeginCall
|
||||
fun getCallsForCache(limit: Int): Cursor {
|
||||
return readableDatabase
|
||||
.query(
|
||||
"""
|
||||
SELECT $TABLE_NAME.*, ${MessageTable.TABLE_NAME}.${MessageTable.BODY}, ${GroupTable.TABLE_NAME}.${GroupTable.V2_DECRYPTED_GROUP}
|
||||
FROM $TABLE_NAME
|
||||
INNER JOIN ${RecipientTable.TABLE_NAME} ON ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} = $PEER
|
||||
LEFT JOIN ${GroupTable.TABLE_NAME} ON ${GroupTable.TABLE_NAME}.${GroupTable.RECIPIENT_ID} = $PEER
|
||||
LEFT JOIN ${MessageTable.TABLE_NAME} ON ${MessageTable.TABLE_NAME}.${MessageTable.ID} = $MESSAGE_ID
|
||||
WHERE ${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED} = 0 AND ${RecipientTable.TABLE_NAME}.${RecipientTable.HIDDEN} = 0 AND $TABLE_NAME.$EVENT != ${Event.DELETE.code}
|
||||
ORDER BY $TIMESTAMP DESC
|
||||
LIMIT $limit
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
|
||||
@@ -1579,7 +1364,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
|
||||
}
|
||||
}
|
||||
|
||||
enum class Type(private val code: Int) {
|
||||
enum class Type(val code: Int) {
|
||||
AUDIO_CALL(0),
|
||||
VIDEO_CALL(1),
|
||||
GROUP_CALL(3),
|
||||
@@ -1611,7 +1396,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
|
||||
}
|
||||
}
|
||||
|
||||
enum class Direction(private val code: Int) {
|
||||
enum class Direction(val code: Int) {
|
||||
INCOMING(0),
|
||||
OUTGOING(1);
|
||||
|
||||
@@ -1652,7 +1437,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
|
||||
}
|
||||
}
|
||||
|
||||
enum class Event(private val code: Int) {
|
||||
enum class Event(val code: Int) {
|
||||
/**
|
||||
* 1:1 Calls only.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user