commit 8c71e4af27c7b08e1a8c0901b5efaa5bba4cb3a2
parent 0994b8f36dd43db0517cfa0d2ae4dc2bfb876701
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Tue, 16 Jan 2024 00:01:37 +0100

perf: bridge objects
- bridge oneway methods
- scope sync

Diffstat:
Mapp/build.gradle.kts | 1+
Mapp/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt | 11+++++------
Mapp/src/main/kotlin/me/rhunk/snapenhance/messaging/ModDatabase.kt | 72+++++++++++++++++++++++++++++++++++++++++-------------------------------
Mapp/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/SocialSection.kt | 15+++++++++++----
Mcommon/build.gradle.kts | 1+
Mcommon/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl | 20++++++++++----------
Mcommon/src/main/aidl/me/rhunk/snapenhance/bridge/scripting/IScripting.aidl | 6+++---
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/ModConfig.kt | 2+-
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/data/MessagingCoreObjects.kt | 23+++++++++++++----------
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/database/impl/FriendInfo.kt | 3+--
Acommon/src/main/kotlin/me/rhunk/snapenhance/common/util/ParcelableExt.kt | 33+++++++++++++++++++++++++++++++++
Dcommon/src/main/kotlin/me/rhunk/snapenhance/common/util/SerializableDataObject.kt | 23-----------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt | 31++++++++++++++++++++++---------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt | 8+++++++-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/ActionManager.kt | 10+++++-----
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt | 163++++++++++++++++++++++++++++++++++++++-----------------------------------------
16 files changed, 233 insertions(+), 189 deletions(-)

diff --git a/app/build.gradle.kts b/app/build.gradle.kts @@ -8,6 +8,7 @@ import java.io.ByteArrayOutputStream plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.kotlinAndroid) + id("kotlin-parcelize") } android { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt @@ -12,9 +12,8 @@ import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.common.data.MessagingFriendInfo import me.rhunk.snapenhance.common.data.MessagingGroupInfo import me.rhunk.snapenhance.common.data.SocialScope -import me.rhunk.snapenhance.common.database.impl.FriendInfo import me.rhunk.snapenhance.common.logger.LogLevel -import me.rhunk.snapenhance.common.util.SerializableDataObject +import me.rhunk.snapenhance.common.util.toParcelable import me.rhunk.snapenhance.download.DownloadProcessor import kotlin.system.measureTimeMillis @@ -61,12 +60,12 @@ class BridgeService : Service() { when (scope) { SocialScope.FRIEND -> { - SerializableDataObject.fromJson<FriendInfo>(syncedObject).let { + toParcelable<MessagingFriendInfo>(syncedObject)?.let { modDatabase.syncFriend(it) } } SocialScope.GROUP -> { - SerializableDataObject.fromJson<MessagingGroupInfo>(syncedObject).let { + toParcelable<MessagingGroupInfo>(syncedObject)?.let { modDatabase.syncGroupInfo(it) } } @@ -169,8 +168,8 @@ class BridgeService : Service() { ) { remoteSideContext.log.verbose("Received ${groups.size} groups and ${friends.size} friends") remoteSideContext.modDatabase.receiveMessagingDataCallback( - friends.map { SerializableDataObject.fromJson<MessagingFriendInfo>(it) }, - groups.map { SerializableDataObject.fromJson<MessagingGroupInfo>(it) } + friends.mapNotNull { toParcelable<MessagingFriendInfo>(it) }, + groups.mapNotNull { toParcelable<MessagingGroupInfo>(it) } ) } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/messaging/ModDatabase.kt b/app/src/main/kotlin/me/rhunk/snapenhance/messaging/ModDatabase.kt @@ -6,7 +6,6 @@ import me.rhunk.snapenhance.common.data.FriendStreaks import me.rhunk.snapenhance.common.data.MessagingFriendInfo import me.rhunk.snapenhance.common.data.MessagingGroupInfo import me.rhunk.snapenhance.common.data.MessagingRuleType -import me.rhunk.snapenhance.common.database.impl.FriendInfo import me.rhunk.snapenhance.common.scripting.type.ModuleInfo import me.rhunk.snapenhance.common.util.SQLiteDatabaseHelper import me.rhunk.snapenhance.common.util.ktx.getInteger @@ -56,7 +55,7 @@ class ModDatabase( "targetUuid VARCHAR" ), "streaks" to listOf( - "userId VARCHAR PRIMARY KEY", + "id VARCHAR PRIMARY KEY", "notify BOOLEAN", "expirationTimestamp BIGINT", "length INTEGER" @@ -78,10 +77,10 @@ class ModDatabase( while (cursor.moveToNext()) { groups.add( MessagingGroupInfo( - conversationId = cursor.getStringOrNull("conversationId")!!, - name = cursor.getStringOrNull("name")!!, - participantsCount = cursor.getInteger("participantsCount") - ) + conversationId = cursor.getStringOrNull("conversationId")!!, + name = cursor.getStringOrNull("name")!!, + participantsCount = cursor.getInteger("participantsCount") + ) ) } groups @@ -89,18 +88,25 @@ class ModDatabase( } fun getFriends(descOrder: Boolean = false): List<MessagingFriendInfo> { - return database.rawQuery("SELECT * FROM friends ORDER BY id ${if (descOrder) "DESC" else "ASC"}", null).use { cursor -> + return database.rawQuery("SELECT * FROM friends LEFT OUTER JOIN streaks ON friends.userId = streaks.id ORDER BY id ${if (descOrder) "DESC" else "ASC"}", null).use { cursor -> val friends = mutableListOf<MessagingFriendInfo>() while (cursor.moveToNext()) { runCatching { friends.add( MessagingFriendInfo( - userId = cursor.getStringOrNull("userId")!!, - displayName = cursor.getStringOrNull("displayName"), - mutableUsername = cursor.getStringOrNull("mutableUsername")!!, - bitmojiId = cursor.getStringOrNull("bitmojiId"), - selfieId = cursor.getStringOrNull("selfieId") - ) + userId = cursor.getStringOrNull("userId")!!, + displayName = cursor.getStringOrNull("displayName"), + mutableUsername = cursor.getStringOrNull("mutableUsername")!!, + bitmojiId = cursor.getStringOrNull("bitmojiId"), + selfieId = cursor.getStringOrNull("selfieId"), + streaks = cursor.getLongOrNull("expirationTimestamp")?.let { + FriendStreaks( + notify = cursor.getInteger("notify") == 1, + expirationTimestamp = it, + length = cursor.getInteger("length") + ) + } + ) ) }.onFailure { context.log.error("Failed to parse friend", it) @@ -125,7 +131,7 @@ class ModDatabase( } } - fun syncFriend(friend: FriendInfo) { + fun syncFriend(friend: MessagingFriendInfo) { executeAsync { try { database.execSQL( @@ -133,24 +139,22 @@ class ModDatabase( arrayOf( friend.userId, friend.displayName, - friend.usernameForSorting!!, - friend.bitmojiAvatarId, - friend.bitmojiSelfieId + friend.mutableUsername, + friend.bitmojiId, + friend.selfieId ) ) //sync streaks - if (friend.streakLength > 0) { - val streaks = getFriendStreaks(friend.userId!!) + friend.streaks?.takeIf { it.length > 0 }?.let { + val streaks = getFriendStreaks(friend.userId) - database.execSQL("INSERT OR REPLACE INTO streaks (userId, notify, expirationTimestamp, length) VALUES (?, ?, ?, ?)", arrayOf( + database.execSQL("INSERT OR REPLACE INTO streaks (id, notify, expirationTimestamp, length) VALUES (?, ?, ?, ?)", arrayOf( friend.userId, streaks?.notify ?: true, - friend.streakExpirationTimestamp, - friend.streakLength + it.expirationTimestamp, + it.length )) - } else { - database.execSQL("DELETE FROM streaks WHERE userId = ?", arrayOf(friend.userId)) - } + } ?: database.execSQL("DELETE FROM streaks WHERE id = ?", arrayOf(friend.userId)) } catch (e: Exception) { throw e } @@ -190,14 +194,21 @@ class ModDatabase( } fun getFriendInfo(userId: String): MessagingFriendInfo? { - return database.rawQuery("SELECT * FROM friends WHERE userId = ?", arrayOf(userId)).use { cursor -> + return database.rawQuery("SELECT * FROM friends LEFT OUTER JOIN streaks ON friends.userId = streaks.id WHERE userId = ?", arrayOf(userId)).use { cursor -> if (!cursor.moveToFirst()) return@use null MessagingFriendInfo( userId = cursor.getStringOrNull("userId")!!, displayName = cursor.getStringOrNull("displayName"), mutableUsername = cursor.getStringOrNull("mutableUsername")!!, bitmojiId = cursor.getStringOrNull("bitmojiId"), - selfieId = cursor.getStringOrNull("selfieId") + selfieId = cursor.getStringOrNull("selfieId"), + streaks = cursor.getLongOrNull("expirationTimestamp")?.let { + FriendStreaks( + notify = cursor.getInteger("notify") == 1, + expirationTimestamp = it, + length = cursor.getInteger("length") + ) + } ) } } @@ -205,7 +216,7 @@ class ModDatabase( fun deleteFriend(userId: String) { executeAsync { database.execSQL("DELETE FROM friends WHERE userId = ?", arrayOf(userId)) - database.execSQL("DELETE FROM streaks WHERE userId = ?", arrayOf(userId)) + database.execSQL("DELETE FROM streaks WHERE id = ?", arrayOf(userId)) database.execSQL("DELETE FROM rules WHERE targetUuid = ?", arrayOf(userId)) } } @@ -229,10 +240,9 @@ class ModDatabase( } fun getFriendStreaks(userId: String): FriendStreaks? { - return database.rawQuery("SELECT * FROM streaks WHERE userId = ?", arrayOf(userId)).use { cursor -> + return database.rawQuery("SELECT * FROM streaks WHERE id = ?", arrayOf(userId)).use { cursor -> if (!cursor.moveToFirst()) return@use null FriendStreaks( - userId = cursor.getStringOrNull("userId")!!, notify = cursor.getInteger("notify") == 1, expirationTimestamp = cursor.getLongOrNull("expirationTimestamp") ?: 0L, length = cursor.getInteger("length") @@ -242,7 +252,7 @@ class ModDatabase( fun setFriendStreaksNotify(userId: String, notify: Boolean) { executeAsync { - database.execSQL("UPDATE streaks SET notify = ? WHERE userId = ?", arrayOf( + database.execSQL("UPDATE streaks SET notify = ? WHERE id = ?", arrayOf( if (notify) 1 else 0, userId )) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/SocialSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/SocialSection.kt @@ -26,7 +26,9 @@ import androidx.compose.ui.window.Dialog import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.navigation +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import me.rhunk.snapenhance.R import me.rhunk.snapenhance.common.data.MessagingFriendInfo import me.rhunk.snapenhance.common.data.MessagingGroupInfo @@ -214,8 +216,13 @@ class SocialSection : Section() { SocialScope.FRIEND -> { val friend = friendList[index] - val streaks = - remember { context.modDatabase.getFriendStreaks(friend.userId) } + var streaks by remember { mutableStateOf(friend.streaks) } + + LaunchedEffect(friend.userId) { + withContext(Dispatchers.IO) { + streaks = context.modDatabase.getFriendStreaks(friend.userId) + } + } BitmojiImage( context = context, @@ -244,7 +251,7 @@ class SocialSection : Section() { ) } Row(verticalAlignment = Alignment.CenterVertically) { - if (streaks != null && streaks.notify) { + streaks?.takeIf { it.notify }?.let { streaks -> Icon( imageVector = ImageVector.vectorResource(id = R.drawable.streak_icon), contentDescription = null, @@ -256,7 +263,7 @@ class SocialSection : Section() { Text( text = context.translation.format( "manager.sections.social.streaks_expiration_short", - "hours" to ((streaks.expirationTimestamp - System.currentTimeMillis()) / 3600000).toInt() + "hours" to (((streaks.expirationTimestamp - System.currentTimeMillis()) / 3600000).toInt().takeIf { it > 0 } ?: 0) .toString() ), maxLines = 1, diff --git a/common/build.gradle.kts b/common/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.androidLibrary) alias(libs.plugins.kotlinAndroid) + id("kotlin-parcelize") } android { diff --git a/common/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl b/common/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl @@ -13,7 +13,7 @@ interface BridgeInterface { /** * broadcast a log message */ - void broadcastLog(String tag, String level, String message); + oneway void broadcastLog(String tag, String level, String message); /** * Execute a file operation @@ -36,7 +36,7 @@ interface BridgeInterface { /** * Enqueue a download */ - void enqueueDownload(in Intent intent, DownloadCallback callback); + oneway void enqueueDownload(in Intent intent, DownloadCallback callback); /** * Get rules for a given user or conversation @@ -56,7 +56,7 @@ interface BridgeInterface { * * @param type rule type (MessagingRuleType) */ - void setRule(String uuid, String type, boolean state); + oneway void setRule(String uuid, String type, boolean state); /** * Sync groups and friends @@ -66,12 +66,12 @@ interface BridgeInterface { /** * Trigger sync for an id */ - void triggerSync(String scope, String id); + oneway void triggerSync(String scope, String id); /** * Pass all groups and friends to be able to add them to the database - * @param groups list of groups (MessagingGroupInfo as json string) - * @param friends list of friends (MessagingFriendInfo as json string) + * @param groups list of groups (MessagingGroupInfo as parcelable) + * @param friends list of friends (MessagingFriendInfo as parcelable) */ oneway void passGroupsAndFriends(in List<String> groups, in List<String> friends); @@ -81,11 +81,11 @@ interface BridgeInterface { MessageLoggerInterface getMessageLogger(); - void registerMessagingBridge(MessagingBridge bridge); + oneway void registerMessagingBridge(MessagingBridge bridge); - void openSettingsOverlay(); + oneway void openSettingsOverlay(); - void closeSettingsOverlay(); + oneway void closeSettingsOverlay(); - void registerConfigStateListener(in ConfigStateListener listener); + oneway void registerConfigStateListener(in ConfigStateListener listener); } \ No newline at end of file diff --git a/common/src/main/aidl/me/rhunk/snapenhance/bridge/scripting/IScripting.aidl b/common/src/main/aidl/me/rhunk/snapenhance/bridge/scripting/IScripting.aidl @@ -8,11 +8,11 @@ interface IScripting { @nullable String getScriptContent(String path); - void registerIPCListener(String channel, String eventName, IPCListener listener); + oneway void registerIPCListener(String channel, String eventName, IPCListener listener); - void sendIPCMessage(String channel, String eventName, in String[] args); + oneway void sendIPCMessage(String channel, String eventName, in String[] args); @nullable String configTransaction(String module, String action, @nullable String key, @nullable String value, boolean save); - void registerAutoReloadListener(in AutoReloadListener listener); + oneway void registerAutoReloadListener(in AutoReloadListener listener); } \ No newline at end of file diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/ModConfig.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/ModConfig.kt @@ -33,7 +33,7 @@ class ModConfig( private fun load() { root = createRootConfig() wasPresent = file.isFileExists() - if (!file.isFileExists()) { + if (!wasPresent) { writeConfig() return } diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/data/MessagingCoreObjects.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/data/MessagingCoreObjects.kt @@ -1,7 +1,8 @@ package me.rhunk.snapenhance.common.data +import android.os.Parcelable +import kotlinx.parcelize.Parcelize import me.rhunk.snapenhance.common.config.FeatureNotice -import me.rhunk.snapenhance.common.util.SerializableDataObject import kotlin.time.Duration.Companion.hours @@ -52,12 +53,12 @@ enum class MessagingRuleType( } } +@Parcelize data class FriendStreaks( - val userId: String, - val notify: Boolean, + val notify: Boolean = true, val expirationTimestamp: Long, val length: Int -) : SerializableDataObject() { +): Parcelable { fun hoursLeft() = (expirationTimestamp - System.currentTimeMillis()) / 1000 / 60 / 60 fun isAboutToExpire(expireHours: Int) = (expirationTimestamp - System.currentTimeMillis()).let { @@ -65,20 +66,22 @@ data class FriendStreaks( } } +@Parcelize data class MessagingGroupInfo( val conversationId: String, val name: String, val participantsCount: Int -) : SerializableDataObject() +): Parcelable +@Parcelize data class MessagingFriendInfo( val userId: String, val displayName: String?, val mutableUsername: String, val bitmojiId: String?, - val selfieId: String? -) : SerializableDataObject() - + val selfieId: String?, + var streaks: FriendStreaks?, +): Parcelable class StoryData( val url: String, @@ -86,4 +89,4 @@ class StoryData( val createdAt: Long, val key: ByteArray?, val iv: ByteArray? -) : SerializableDataObject()- \ No newline at end of file +)+ \ No newline at end of file diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/database/impl/FriendInfo.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/database/impl/FriendInfo.kt @@ -3,7 +3,6 @@ package me.rhunk.snapenhance.common.database.impl import android.annotation.SuppressLint import android.database.Cursor import me.rhunk.snapenhance.common.database.DatabaseObject -import me.rhunk.snapenhance.common.util.SerializableDataObject import me.rhunk.snapenhance.common.util.ktx.getInteger import me.rhunk.snapenhance.common.util.ktx.getLong import me.rhunk.snapenhance.common.util.ktx.getStringOrNull @@ -33,7 +32,7 @@ data class FriendInfo( var usernameForSorting: String? = null, var friendLinkType: Int = 0, var postViewEmoji: String? = null, -) : DatabaseObject, SerializableDataObject() { +) : DatabaseObject { val mutableUsername get() = username?.split("|")?.last() val firstCreatedUsername get() = username?.split("|")?.first() diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/util/ParcelableExt.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/util/ParcelableExt.kt @@ -0,0 +1,33 @@ +package me.rhunk.snapenhance.common.util + +import android.os.Parcelable +import kotlinx.parcelize.parcelableCreator +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi + +@OptIn(ExperimentalEncodingApi::class) +fun Parcelable.toSerialized(): String? { + val parcel = android.os.Parcel.obtain() + return try { + writeToParcel(parcel, 0) + parcel.marshall()?.let { + Base64.encode(it) + } + } finally { + parcel.recycle() + } +} + +@OptIn(ExperimentalEncodingApi::class) +inline fun <reified T : Parcelable> toParcelable(serialized: String): T? { + val parcel = android.os.Parcel.obtain() + return try { + Base64.decode(serialized).let { + parcel.unmarshall(it, 0, it.size) + } + parcel.setDataPosition(0) + parcelableCreator<T>().createFromParcel(parcel) + } finally { + parcel.recycle() + } +} diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/util/SerializableDataObject.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/util/SerializableDataObject.kt @@ -1,22 +0,0 @@ -package me.rhunk.snapenhance.common.util - -import com.google.gson.Gson -import com.google.gson.GsonBuilder - -open class SerializableDataObject { - companion object { - val gson: Gson = GsonBuilder().create() - - inline fun <reified T : SerializableDataObject> fromJson(json: String): T { - return gson.fromJson(json, T::class.java) - } - - inline fun <reified T : SerializableDataObject> fromJson(json: String, type: Class<T>): T { - return gson.fromJson(json, type) - } - } - - fun toJson(): String { - return gson.toJson(this) - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt @@ -13,8 +13,10 @@ import me.rhunk.snapenhance.bridge.SyncCallback import me.rhunk.snapenhance.common.Constants import me.rhunk.snapenhance.common.ReceiversConfig import me.rhunk.snapenhance.common.action.EnumAction +import me.rhunk.snapenhance.common.data.FriendStreaks import me.rhunk.snapenhance.common.data.MessagingFriendInfo import me.rhunk.snapenhance.common.data.MessagingGroupInfo +import me.rhunk.snapenhance.common.util.toSerialized import me.rhunk.snapenhance.core.bridge.BridgeClient import me.rhunk.snapenhance.core.bridge.loadFromBridge import me.rhunk.snapenhance.core.data.SnapClassCache @@ -151,7 +153,6 @@ class SnapEnhance { features.init() scriptRuntime.connect(bridgeClient.getScriptingInterface()) scriptRuntime.eachModule { callFunction("module.onSnapApplicationLoad", androidContext) } - syncRemote() } } @@ -195,7 +196,7 @@ class SnapEnhance { } } - appContext.apply { + appContext.executeAsync { bridgeClient.registerConfigStateListener(object: ConfigStateListener.Stub() { override fun onConfigChanged() { log.verbose("onConfigChanged") @@ -227,7 +228,21 @@ class SnapEnhance { appContext.executeAsync { bridgeClient.sync(object : SyncCallback.Stub() { override fun syncFriend(uuid: String): String? { - return database.getFriendInfo(uuid)?.toJson() + return database.getFriendInfo(uuid)?.let { + MessagingFriendInfo( + userId = it.userId!!, + displayName = it.displayName, + mutableUsername = it.mutableUsername!!, + bitmojiId = it.bitmojiAvatarId, + selfieId = it.bitmojiSelfieId, + streaks = if (it.streakLength > 0) { + FriendStreaks( + expirationTimestamp = it.streakExpirationTimestamp, + length = it.streakLength + ) + } else null + ).toSerialized() + } } override fun syncGroup(uuid: String): String? { @@ -236,7 +251,7 @@ class SnapEnhance { it.key!!, it.feedDisplayName!!, it.participantsSize - ).toJson() + ).toSerialized() } } }) @@ -260,14 +275,12 @@ class SnapEnhance { it.friendDisplayName, it.friendDisplayUsername!!.split("|")[1], it.bitmojiAvatarId, - it.bitmojiSelfieId + it.bitmojiSelfieId, + streaks = null ) } - bridgeClient.passGroupsAndFriends( - groups.map { it.toJson() }, - friends.map { it.toJson() } - ) + bridgeClient.passGroupsAndFriends(groups, friends) } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/BridgeClient.kt @@ -23,8 +23,11 @@ import me.rhunk.snapenhance.common.bridge.FileLoaderWrapper import me.rhunk.snapenhance.common.bridge.types.BridgeFileType import me.rhunk.snapenhance.common.bridge.types.FileActionType import me.rhunk.snapenhance.common.bridge.types.LocalePair +import me.rhunk.snapenhance.common.data.MessagingFriendInfo +import me.rhunk.snapenhance.common.data.MessagingGroupInfo import me.rhunk.snapenhance.common.data.MessagingRuleType import me.rhunk.snapenhance.common.data.SocialScope +import me.rhunk.snapenhance.common.util.toSerialized import me.rhunk.snapenhance.core.ModContext import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors @@ -136,7 +139,10 @@ class BridgeClient( fun triggerSync(scope: SocialScope, id: String) = service.triggerSync(scope.key, id) - fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends) + fun passGroupsAndFriends(groups: List<MessagingGroupInfo>, friends: List<MessagingFriendInfo>) = service.passGroupsAndFriends( + groups.mapNotNull { it.toSerialized() }, + friends.mapNotNull { it.toSerialized() } + ) fun getRules(targetUuid: String): List<MessagingRuleType> { return service.getRules(targetUuid).mapNotNull { MessagingRuleType.getByName(it) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/ActionManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/ActionManager.kt @@ -15,12 +15,12 @@ class ActionManager( private val actions by lazy { mapOf( - EnumAction.CLEAN_CACHE to CleanCache::class, - EnumAction.EXPORT_CHAT_MESSAGES to ExportChatMessages::class, - EnumAction.BULK_MESSAGING_ACTION to BulkMessagingAction::class, - EnumAction.EXPORT_MEMORIES to ExportMemories::class, + EnumAction.CLEAN_CACHE to CleanCache(), + EnumAction.EXPORT_CHAT_MESSAGES to ExportChatMessages(), + EnumAction.BULK_MESSAGING_ACTION to BulkMessagingAction(), + EnumAction.EXPORT_MEMORIES to ExportMemories(), ).map { - it.key to it.value.java.getConstructor().newInstance().apply { + it.key to it.value.apply { this.context = modContext } }.toMap().toMutableMap() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt @@ -8,17 +8,15 @@ import me.rhunk.snapenhance.core.features.Feature import me.rhunk.snapenhance.core.features.FeatureLoadParams import me.rhunk.snapenhance.core.features.MessagingRuleFeature import me.rhunk.snapenhance.core.features.impl.ConfigurationOverride +import me.rhunk.snapenhance.core.features.impl.MixerStories import me.rhunk.snapenhance.core.features.impl.OperaViewerParamsOverride import me.rhunk.snapenhance.core.features.impl.ScopeSync -import me.rhunk.snapenhance.core.features.impl.MixerStories import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.core.features.impl.downloader.ProfilePictureDownloader import me.rhunk.snapenhance.core.features.impl.experiments.* import me.rhunk.snapenhance.core.features.impl.global.* import me.rhunk.snapenhance.core.features.impl.messaging.* -import me.rhunk.snapenhance.core.features.impl.spying.HalfSwipeNotifier -import me.rhunk.snapenhance.core.features.impl.spying.MessageLogger -import me.rhunk.snapenhance.core.features.impl.spying.StealthMode +import me.rhunk.snapenhance.core.features.impl.spying.* import me.rhunk.snapenhance.core.features.impl.tweaks.BypassScreenshotDetection import me.rhunk.snapenhance.core.features.impl.tweaks.CameraTweaks import me.rhunk.snapenhance.core.features.impl.tweaks.PreventMessageListAutoScroll @@ -33,23 +31,19 @@ import kotlin.system.measureTimeMillis class FeatureManager( private val context: ModContext ) : Manager { - private val features = mutableListOf<Feature>() + private val features = mutableMapOf<KClass<out Feature>, Feature>() - private fun register(vararg featureClasses: KClass<out Feature>) { + private fun register(vararg featureList: Feature) { runBlocking { - featureClasses.forEach { clazz -> + featureList.forEach { feature -> launch(Dispatchers.IO) { runCatching { - clazz.java.constructors.first().newInstance() - .let { it as Feature } - .also { - it.context = context - synchronized(features) { - features.add(it) - } - } + feature.context = context + synchronized(features) { + features[feature::class] = feature + } }.onFailure { - CoreLogger.xposedLog("Failed to register feature ${clazz.simpleName}", it) + CoreLogger.xposedLog("Failed to register feature ${feature.featureKey}", it) } } } @@ -58,91 +52,92 @@ class FeatureManager( @Suppress("UNCHECKED_CAST") fun <T : Feature> get(featureClass: KClass<T>): T? { - return features.find { it::class == featureClass } as? T + return features[featureClass] as? T } - fun getRuleFeatures() = features.filterIsInstance<MessagingRuleFeature>().sortedBy { it.ruleType.ordinal } + fun getRuleFeatures() = features.values.filterIsInstance<MessagingRuleFeature>().sortedBy { it.ruleType.ordinal } override fun init() { register( - EndToEndEncryption::class, - ScopeSync::class, - PreventMessageListAutoScroll::class, - Messaging::class, - MediaDownloader::class, - StealthMode::class, - MenuViewInjector::class, - PreventReadReceipts::class, - MessageLogger::class, - ConvertMessageLocally::class, - SnapchatPlus::class, - DisableMetrics::class, - PreventMessageSending::class, - Notifications::class, - AutoSave::class, - UITweaks::class, - ConfigurationOverride::class, - UnsaveableMessages::class, - SendOverride::class, - UnlimitedSnapViewTime::class, - BypassVideoLengthRestriction::class, - MediaQualityLevelOverride::class, - MeoPasscodeBypass::class, - AppPasscode::class, - LocationSpoofer::class, - CameraTweaks::class, - InfiniteStoryBoost::class, - AmoledDarkMode::class, - PinConversations::class, - UnlimitedMultiSnap::class, - DeviceSpooferHook::class, - ClientBootstrapOverride::class, - GooglePlayServicesDialogs::class, - NoFriendScoreDelay::class, - ProfilePictureDownloader::class, - AddFriendSourceSpoof::class, - DisableReplayInFF::class, - OldBitmojiSelfie::class, - FriendFeedMessagePreview::class, - HideStreakRestore::class, - HideFriendFeedEntry::class, - HideQuickAddFriendFeed::class, - CallStartConfirmation::class, - SnapPreview::class, - InstantDelete::class, - BypassScreenshotDetection::class, - HalfSwipeNotifier::class, - DisableConfirmationDialogs::class, - MixerStories::class, - DisableComposerModules::class, - FideliusIndicator::class, - EditTextOverride::class, - PreventForcedLogout::class, - SuspendLocationUpdates::class, - ConversationToolbox::class, - SpotlightCommentsUsername::class, - OperaViewerParamsOverride::class, + EndToEndEncryption(), + ScopeSync(), + PreventMessageListAutoScroll(), + Messaging(), + MediaDownloader(), + StealthMode(), + MenuViewInjector(), + PreventReadReceipts(), + MessageLogger(), + ConvertMessageLocally(), + SnapchatPlus(), + DisableMetrics(), + PreventMessageSending(), + Notifications(), + AutoSave(), + UITweaks(), + ConfigurationOverride(), + UnsaveableMessages(), + SendOverride(), + UnlimitedSnapViewTime(), + BypassVideoLengthRestriction(), + MediaQualityLevelOverride(), + MeoPasscodeBypass(), + AppPasscode(), + LocationSpoofer(), + CameraTweaks(), + InfiniteStoryBoost(), + AmoledDarkMode(), + PinConversations(), + UnlimitedMultiSnap(), + DeviceSpooferHook(), + ClientBootstrapOverride(), + GooglePlayServicesDialogs(), + NoFriendScoreDelay(), + ProfilePictureDownloader(), + AddFriendSourceSpoof(), + DisableReplayInFF(), + OldBitmojiSelfie(), + FriendFeedMessagePreview(), + HideStreakRestore(), + HideFriendFeedEntry(), + HideQuickAddFriendFeed(), + CallStartConfirmation(), + SnapPreview(), + InstantDelete(), + BypassScreenshotDetection(), + HalfSwipeNotifier(), + DisableConfirmationDialogs(), + MixerStories(), + DisableComposerModules(), + FideliusIndicator(), + EditTextOverride(), + PreventForcedLogout(), + SuspendLocationUpdates(), + ConversationToolbox(), + SpotlightCommentsUsername(), + OperaViewerParamsOverride(), ) initializeFeatures() } + private inline fun tryInit(feature: Feature, crossinline block: () -> Unit) { + runCatching { + block() + }.onFailure { + context.log.error("Failed to init feature ${feature.featureKey}", it) + context.longToast("Failed to init feature ${feature.featureKey}! Check logcat for more details.") + } + } + private fun initFeatures( syncParam: Int, asyncParam: Int, syncAction: (Feature) -> Unit, asyncAction: (Feature) -> Unit ) { - fun tryInit(feature: Feature, block: () -> Unit) { - runCatching { - block() - }.onFailure { - context.log.error("Failed to init feature ${feature.featureKey}", it) - context.longToast("Failed to init feature ${feature.featureKey}! Check logcat for more details.") - } - } - features.toList().forEach { feature -> + features.values.toList().forEach { feature -> if (feature.loadParams and syncParam != 0) { tryInit(feature) { syncAction(feature)