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