commit e82b91eee06078474bc4d45ce28795b37450748e
parent b65109447553a317ff8acb1f67dd053d1da77519
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Tue, 24 Oct 2023 00:04:40 +0200

perf(core): database access

Diffstat:
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/database/DatabaseAccess.kt | 171+++++++++++++++++++++++++++++++++++++------------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/ui/menu/impl/FriendFeedInfoMenu.kt | 15+++++++++------
2 files changed, 88 insertions(+), 98 deletions(-)

diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/database/DatabaseAccess.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/database/DatabaseAccess.kt @@ -12,59 +12,65 @@ import me.rhunk.snapenhance.common.util.ktx.getStringOrNull import me.rhunk.snapenhance.core.ModContext import me.rhunk.snapenhance.core.logger.CoreLogger import me.rhunk.snapenhance.core.manager.Manager -import java.io.File +import java.lang.ref.WeakReference -@SuppressLint("Range") -class DatabaseAccess(private val context: ModContext) : Manager { - private val databaseLock = Any() - - private val arroyoDatabase: File by lazy { - context.androidContext.getDatabasePath("arroyo.db") - } - - private val mainDatabase: File by lazy { - context.androidContext.getDatabasePath("main.db") +inline fun <T> SQLiteDatabase.performOperation(crossinline query: SQLiteDatabase.() -> T?): T? { + synchronized(this) { + if (!isOpen) { + return null + } + return runCatching { + query() + }.onFailure { + CoreLogger.xposedLog("Database operation failed", it) + }.getOrNull() } +} +@SuppressLint("Range") +class DatabaseAccess( + private val context: ModContext +) : Manager { private val dmOtherParticipantCache by lazy { - getAllDMOtherParticipants().toMutableMap() + (openArroyo().performOperation { + rawQuery( + "SELECT client_conversation_id, user_id FROM user_conversation WHERE conversation_type = 0 AND user_id != ?", + arrayOf(myUserId) + ).use { query -> + val participants = mutableMapOf<String, String?>() + if (!query.moveToFirst()) { + return@performOperation null + } + do { + participants[query.getString(query.getColumnIndex("client_conversation_id"))] = + query.getString(query.getColumnIndex("user_id")) + } while (query.moveToNext()) + participants + } + } ?: emptyMap()).toMutableMap() } - private fun openMain(): SQLiteDatabase { - return SQLiteDatabase.openDatabase( - mainDatabase.absolutePath, - null, - SQLiteDatabase.OPEN_READONLY - )!! - } + private var databaseWeakMap = mutableMapOf<String, WeakReference<SQLiteDatabase>?>() + + private fun openLocalDatabase(fileName: String): SQLiteDatabase { + if (databaseWeakMap.containsKey(fileName)) { + val database = databaseWeakMap[fileName]?.get() + if (database != null && database.isOpen) return database + } - private fun openArroyo(): SQLiteDatabase { return SQLiteDatabase.openDatabase( - arroyoDatabase.absolutePath, + context.androidContext.getDatabasePath(fileName).absolutePath, null, SQLiteDatabase.OPEN_READONLY - )!! + )?.also { + databaseWeakMap[fileName] = WeakReference(it) + } ?: throw IllegalStateException("Failed to open database $fileName") } - fun hasArroyo(): Boolean { - return arroyoDatabase.exists() - } + private fun openMain() = openLocalDatabase("main.db") + private fun openArroyo() = openLocalDatabase("arroyo.db") - private fun <T> safeDatabaseOperation( - database: SQLiteDatabase, - query: (SQLiteDatabase) -> T? - ): T? { - synchronized(databaseLock) { - if (!database.isOpen) { - return null - } - return runCatching { - query(database) - }.onFailure { - CoreLogger.xposedLog("Database operation failed", it) - }.getOrNull() - } - } + fun hasArroyo(): Boolean = context.androidContext.getDatabasePath("arroyo.db").exists() private fun <T : DatabaseObject> readDatabaseObject( obj: T, @@ -85,10 +91,10 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getFeedEntryByUserId(userId: String): FriendFeedEntry? { - return safeDatabaseOperation(openMain()) { database -> + return openMain().performOperation { readDatabaseObject( FriendFeedEntry(), - database, + this, "FriendsFeedView", "friendUserId = ?", arrayOf(userId) @@ -97,23 +103,23 @@ class DatabaseAccess(private val context: ModContext) : Manager { } val myUserId by lazy { - safeDatabaseOperation(openArroyo()) { arroyoDatabase: SQLiteDatabase -> - arroyoDatabase.rawQuery(buildString { + openArroyo().performOperation { + rawQuery(buildString { append("SELECT value FROM required_values WHERE key = 'USERID'") }, null).use { query -> if (!query.moveToFirst()) { - return@safeDatabaseOperation null + return@performOperation null } query.getStringOrNull("value")!! } - }!! + } ?: context.androidContext.getSharedPreferences("user_session_shared_pref", 0).getString("key_user_id", null)!! } fun getFeedEntryByConversationId(conversationId: String): FriendFeedEntry? { - return safeDatabaseOperation(openMain()) { + return openMain().performOperation { readDatabaseObject( FriendFeedEntry(), - it, + this, "FriendsFeedView", "key = ?", arrayOf(conversationId) @@ -122,10 +128,10 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getFriendInfo(userId: String): FriendInfo? { - return safeDatabaseOperation(openMain()) { + return openMain().performOperation { readDatabaseObject( FriendInfo(), - it, + this, "FriendWithUsername", "userId = ?", arrayOf(userId) @@ -134,8 +140,8 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getFeedEntries(limit: Int): List<FriendFeedEntry> { - return safeDatabaseOperation(openMain()) { database -> - database.rawQuery( + return openMain().performOperation { + rawQuery( "SELECT * FROM FriendsFeedView ORDER BY _id LIMIT ?", arrayOf(limit.toString()) ).use { query -> @@ -153,10 +159,10 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getConversationMessageFromId(clientMessageId: Long): ConversationMessage? { - return safeDatabaseOperation(openArroyo()) { + return openArroyo().performOperation { readDatabaseObject( ConversationMessage(), - it, + this, "conversation_message", "client_message_id = ?", arrayOf(clientMessageId.toString()) @@ -165,13 +171,13 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getConversationType(conversationId: String): Int? { - return safeDatabaseOperation(openArroyo()) { database -> - database.rawQuery( + return openArroyo().performOperation { + rawQuery( "SELECT conversation_type FROM user_conversation WHERE client_conversation_id = ?", arrayOf(conversationId) ).use { query -> if (!query.moveToFirst()) { - return@safeDatabaseOperation null + return@performOperation null } query.getInt(query.getColumnIndex("conversation_type")) } @@ -179,10 +185,10 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getConversationLinkFromUserId(userId: String): UserConversationLink? { - return safeDatabaseOperation(openArroyo()) { + return openArroyo().performOperation { readDatabaseObject( UserConversationLink(), - it, + this, "user_conversation", "user_id = ? AND conversation_type = 0", arrayOf(userId) @@ -190,35 +196,16 @@ class DatabaseAccess(private val context: ModContext) : Manager { } } - private fun getAllDMOtherParticipants(): Map<String, String?> { - return safeDatabaseOperation(openArroyo()) { arroyoDatabase: SQLiteDatabase -> - arroyoDatabase.rawQuery( - "SELECT client_conversation_id, user_id FROM user_conversation WHERE conversation_type = 0", - null - ).use { query -> - val participants = mutableMapOf<String, String>() - if (!query.moveToFirst()) { - return@safeDatabaseOperation null - } - do { - participants[query.getString(query.getColumnIndex("client_conversation_id"))] = - query.getString(query.getColumnIndex("user_id")) - } while (query.moveToNext()) - participants - } - } ?: emptyMap() - } - fun getDMOtherParticipant(conversationId: String): String? { if (dmOtherParticipantCache.containsKey(conversationId)) return dmOtherParticipantCache[conversationId] - return safeDatabaseOperation(openArroyo()) { cursor -> - cursor.rawQuery( + return openArroyo().performOperation { + rawQuery( "SELECT user_id FROM user_conversation WHERE client_conversation_id = ? AND conversation_type = 0", arrayOf(conversationId) ).use { query -> val participants = mutableListOf<String>() if (!query.moveToFirst()) { - return@safeDatabaseOperation null + return@performOperation null } do { participants.add(query.getString(query.getColumnIndex("user_id"))) @@ -230,19 +217,19 @@ class DatabaseAccess(private val context: ModContext) : Manager { fun getStoryEntryFromId(storyId: String): StoryEntry? { - return safeDatabaseOperation(openMain()) { - readDatabaseObject(StoryEntry(), it, "Story", "storyId = ?", arrayOf(storyId)) + return openMain().performOperation { + readDatabaseObject(StoryEntry(), this, "Story", "storyId = ?", arrayOf(storyId)) } } fun getConversationParticipants(conversationId: String): List<String>? { - return safeDatabaseOperation(openArroyo()) { arroyoDatabase: SQLiteDatabase -> - arroyoDatabase.rawQuery( + return openArroyo().performOperation { + rawQuery( "SELECT user_id FROM user_conversation WHERE client_conversation_id = ?", arrayOf(conversationId) ).use { if (!it.moveToFirst()) { - return@safeDatabaseOperation null + return@performOperation null } val participants = mutableListOf<String>() do { @@ -257,13 +244,13 @@ class DatabaseAccess(private val context: ModContext) : Manager { conversationId: String, limit: Int ): List<ConversationMessage>? { - return safeDatabaseOperation(openArroyo()) { arroyoDatabase: SQLiteDatabase -> - arroyoDatabase.rawQuery( + return openArroyo().performOperation { + rawQuery( "SELECT * FROM conversation_message WHERE client_conversation_id = ? ORDER BY creation_timestamp DESC LIMIT ?", arrayOf(conversationId, limit.toString()) ).use { query -> if (!query.moveToFirst()) { - return@safeDatabaseOperation null + return@performOperation null } val messages = mutableListOf<ConversationMessage>() do { @@ -277,13 +264,13 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getAddSource(userId: String): String? { - return safeDatabaseOperation(openMain()) { database -> - database.rawQuery( + return openMain().performOperation { + rawQuery( "SELECT addSource FROM FriendWhoAddedMe WHERE userId = ?", arrayOf(userId) ).use { if (!it.moveToFirst()) { - return@safeDatabaseOperation null + return@performOperation null } it.getStringOrNull("addSource") } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/menu/impl/FriendFeedInfoMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/menu/impl/FriendFeedInfoMenu.kt @@ -116,13 +116,16 @@ class FriendFeedInfoMenu : AbstractMenu() { messages.forEach { message -> val sender = participants[message.senderId] - val protoReader = ( - messageLogger.takeIf { it.isEnabled }?.getMessageProto(conversationId, message.clientMessageId.toLong()) ?: ProtoReader(message.messageContent ?: return@forEach).followPath(4, 4) - ) ?: return@forEach - - val contentType = ContentType.fromMessageContainer(protoReader) ?: ContentType.fromId(message.contentType) + val messageProtoReader = + messageLogger.takeIf { + it.isEnabled && message.contentType == ContentType.STATUS.id // only process deleted messages + }?.getMessageProto(conversationId, message.clientMessageId.toLong()) + ?: ProtoReader(message.messageContent ?: return@forEach).followPath(4, 4) + ?: return@forEach + + val contentType = ContentType.fromMessageContainer(messageProtoReader) ?: ContentType.fromId(message.contentType) var messageString = if (contentType == ContentType.CHAT) { - protoReader.getString(2, 1) ?: return@forEach + messageProtoReader.getString(2, 1) ?: return@forEach } else { contentType.name }