commit 97806d693e3cbf8e8cfe57bee4d414a80fe15651 parent 4ecf5f81773dd351c2f99b1dfd8a435f98da4468 Author: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sun, 10 Mar 2024 12:37:11 +0100 refactor: debug properties Diffstat:
15 files changed, 295 insertions(+), 261 deletions(-)
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt @@ -198,7 +198,7 @@ class RemoteSideContext( } } - if (mappings.isMappingsOutdated() || !mappings.isMappingsLoaded) { + if (!sharedPreferences.getBoolean("debug_disable_mapper", false) && (mappings.isMappingsOutdated() || !mappings.isMappingsLoaded)) { requirements = requirements or Requirements.MAPPINGS } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt @@ -202,5 +202,9 @@ class BridgeService : Service() { override fun registerConfigStateListener(listener: ConfigStateListener) { remoteSideContext.config.configStateListener = listener } + + override fun getDebugProp(key: String, defaultValue: String?): String? { + return remoteSideContext.sharedPreferences.all["debug_$key"]?.toString() ?: defaultValue + } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/home/HomeSettings.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/home/HomeSettings.kt @@ -1,5 +1,6 @@ package me.rhunk.snapenhance.ui.manager.pages.home +import android.content.SharedPreferences import androidx.compose.foundation.ScrollState import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -41,6 +42,30 @@ class HomeSettings : Routes.Route() { } @Composable + private fun PreferenceToggle(sharedPreferences: SharedPreferences, key: String, text: String) { + val realKey = "debug_$key" + var value by remember { mutableStateOf(sharedPreferences.getBoolean(realKey, false)) } + + Row( + modifier = Modifier + .fillMaxWidth() + .height(55.dp) + .clickable { + value = !value + sharedPreferences.edit().putBoolean(realKey, value).apply() + }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text(text = text) + Switch(checked = value, onCheckedChange = { + value = it + sharedPreferences.edit().putBoolean(realKey, it).apply() + }, modifier = Modifier.padding(end = 26.dp)) + } + } + + @Composable private fun RowAction(title: String, requireConfirmation: Boolean = false, action: () -> Unit) { var confirmationDialog by remember { mutableStateOf(false) @@ -184,7 +209,9 @@ class HomeSettings : Routes.Route() { } } OutlinedButton( - modifier = Modifier.fillMaxWidth().padding(5.dp), + modifier = Modifier + .fillMaxWidth() + .padding(5.dp), onClick = { routes.loggerHistory.navigate() } @@ -194,7 +221,7 @@ class HomeSettings : Routes.Route() { } } - RowTitle(title = "Clear App Files") + RowTitle(title = "Debug") Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -210,7 +237,7 @@ class HomeSettings : Routes.Route() { ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { expanded = it }, - modifier = Modifier.fillMaxWidth(0.8f) + modifier = Modifier.fillMaxWidth(0.7f) ) { TextField( value = selectedFileType.displayName, @@ -241,7 +268,16 @@ class HomeSettings : Routes.Route() { context.shortToast("Done!") } }) { - Text(text = "Clear") + Text(text = "Clear File") + } + } + ShiftedRow { + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + PreferenceToggle(context.sharedPreferences, key = "disable_feature_loading", text = "Disable Feature Loading") + PreferenceToggle(context.sharedPreferences, key = "disable_mapper", text = "Disable Auto Mapper") + PreferenceToggle(context.sharedPreferences, key = "force_native_load", text = "Force Native Load") } } Spacer(modifier = Modifier.height(50.dp)) diff --git a/common/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl b/common/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl @@ -94,4 +94,6 @@ interface BridgeInterface { oneway void closeSettingsOverlay(); oneway void registerConfigStateListener(in ConfigStateListener listener); + + @nullable String getDebugProp(String key, @nullable String defaultValue); } \ 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 @@ -59,7 +59,7 @@ data class FriendInfo( serverDisplayName = getStringOrNull("serverDisplayName") streakLength = getInteger("streakLength") streakExpirationTimestamp = getLong("streakExpiration") - reverseBestFriendRanking = getInteger("reverseBestFriendRanking") + reverseBestFriendRanking = getIntOrNull("reverseBestFriendRanking") ?: 0 usernameForSorting = getStringOrNull("usernameForSorting") friendLinkType = getInteger("friendLinkType") postViewEmoji = getStringOrNull("postViewEmoji") @@ -68,5 +68,4 @@ data class FriendInfo( plusBadgeVisibility = getIntOrNull("plusBadgeVisibility") ?: 0 } } - } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/ModContext.kt @@ -24,8 +24,8 @@ import me.rhunk.snapenhance.core.event.EventBus import me.rhunk.snapenhance.core.event.EventDispatcher import me.rhunk.snapenhance.core.features.Feature import me.rhunk.snapenhance.core.logger.CoreLogger -import me.rhunk.snapenhance.core.manager.impl.ActionManager -import me.rhunk.snapenhance.core.manager.impl.FeatureManager +import me.rhunk.snapenhance.core.action.ActionManager +import me.rhunk.snapenhance.core.features.FeatureManager import me.rhunk.snapenhance.core.messaging.CoreMessagingBridge import me.rhunk.snapenhance.core.messaging.MessageSender import me.rhunk.snapenhance.core.scripting.CoreScriptRuntime diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt @@ -132,7 +132,6 @@ class SnapEnhance { } reloadConfig() - actionManager.init() initConfigListener() initWidgetListener() initNative() @@ -175,7 +174,10 @@ class SnapEnhance { private fun initNative() { // don't initialize native when not logged in - if (appContext.androidContext.getSharedPreferences("user_session_shared_pref", 0).getString("key_user_id", null) == null) return + if ( + appContext.androidContext.getSharedPreferences("user_session_shared_pref", 0).getString("key_user_id", null) == null && + appContext.bridgeClient.getDebugProp("force_native_load", null) != "true" + ) return if (appContext.config.experimental.nativeHooks.globalState != true) return lateinit var unhook: () -> Unit diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/action/ActionManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/action/ActionManager.kt @@ -0,0 +1,41 @@ +package me.rhunk.snapenhance.core.action + +import android.content.Intent +import me.rhunk.snapenhance.common.action.EnumAction +import me.rhunk.snapenhance.core.ModContext +import me.rhunk.snapenhance.core.action.impl.BulkMessagingAction +import me.rhunk.snapenhance.core.action.impl.CleanCache +import me.rhunk.snapenhance.core.action.impl.ExportChatMessages +import me.rhunk.snapenhance.core.action.impl.ExportMemories + +class ActionManager( + private val modContext: ModContext, +) { + + private val actions by lazy { + mapOf( + 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.apply { + this.context = modContext + } + }.toMap().toMutableMap() + } + + fun onNewIntent(intent: Intent?) { + val action = intent?.getStringExtra(EnumAction.ACTION_PARAMETER) ?: return + intent.removeExtra(EnumAction.ACTION_PARAMETER) + execute(EnumAction.entries.find { it.key == action } ?: return) + } + + fun execute(enumAction: EnumAction) { + val action = actions[enumAction] ?: return + action.run() + if (enumAction.exitOnFinish) { + modContext.forceCloseApp() + } + } +}+ \ No newline at end of file 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 @@ -209,4 +209,6 @@ class BridgeClient( fun closeSettingsOverlay() = safeServiceCall { service.closeSettingsOverlay() } fun registerConfigStateListener(listener: ConfigStateListener) = safeServiceCall { service.registerConfigStateListener(listener) } + + fun getDebugProp(name: String, defaultValue: String? = null) = safeServiceCall { service.getDebugProp(name, defaultValue) } } 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 @@ -17,7 +17,6 @@ import me.rhunk.snapenhance.common.util.ktx.getInteger import me.rhunk.snapenhance.common.util.ktx.getStringOrNull import me.rhunk.snapenhance.common.util.protobuf.ProtoReader import me.rhunk.snapenhance.core.ModContext -import me.rhunk.snapenhance.core.manager.Manager import me.rhunk.snapenhance.nativelib.NativeLib @@ -30,7 +29,7 @@ enum class DatabaseType( class DatabaseAccess( private val context: ModContext -) : Manager { +) { private val openedDatabases = mutableMapOf<DatabaseType, SQLiteDatabase>() private fun useDatabase(database: DatabaseType, writeMode: Boolean = false): SQLiteDatabase? { @@ -118,7 +117,7 @@ class DatabaseAccess( fun hasMain(): Boolean = useDatabase(DatabaseType.MAIN)?.isOpen == true fun hasArroyo(): Boolean = useDatabase(DatabaseType.ARROYO)?.isOpen == true - override fun init() { + fun init() { // perform integrity check on databases DatabaseType.entries.forEach { type -> useDatabase(type, writeMode = true)?.apply { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventDispatcher.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventDispatcher.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup.LayoutParams import me.rhunk.snapenhance.common.util.snap.SnapWidgetBroadcastReceiverHelper import me.rhunk.snapenhance.core.ModContext import me.rhunk.snapenhance.core.event.events.impl.* -import me.rhunk.snapenhance.core.manager.Manager import me.rhunk.snapenhance.core.util.hook.HookStage import me.rhunk.snapenhance.core.util.hook.Hooker import me.rhunk.snapenhance.core.util.hook.hook @@ -23,7 +22,7 @@ import java.nio.ByteBuffer class EventDispatcher( private val context: ModContext -) : Manager { +) { private fun hookViewBinder() { context.mappings.useMapper(ViewBinderMapper::class) { val cachedHooks = mutableListOf<String>() @@ -60,8 +59,7 @@ class EventDispatcher( } - - override fun init() { + fun init() { context.classCache.conversationManager.hook("sendMessageWithContent", HookStage.BEFORE) { param -> context.event.post(SendMessageWithContentEvent( destinations = MessageDestinations(param.arg(0)), diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt @@ -0,0 +1,192 @@ +package me.rhunk.snapenhance.core.features + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import me.rhunk.snapenhance.core.ModContext +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.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.tweaks.BypassScreenshotDetection +import me.rhunk.snapenhance.core.features.impl.tweaks.CameraTweaks +import me.rhunk.snapenhance.core.features.impl.tweaks.DisablePermissionRequests +import me.rhunk.snapenhance.core.features.impl.tweaks.PreventMessageListAutoScroll +import me.rhunk.snapenhance.core.features.impl.tweaks.UnsaveableMessages +import me.rhunk.snapenhance.core.features.impl.ui.* +import me.rhunk.snapenhance.core.logger.CoreLogger +import me.rhunk.snapenhance.core.ui.menu.MenuViewInjector +import kotlin.reflect.KClass +import kotlin.system.measureTimeMillis + +class FeatureManager( + private val context: ModContext +) { + private val features = mutableMapOf<KClass<out Feature>, Feature>() + + private fun register(vararg featureList: Feature) { + if (context.bridgeClient.getDebugProp("disable_feature_loading") == "true") { + context.log.warn("Feature loading is disabled") + return + } + + runBlocking { + featureList.forEach { feature -> + launch(Dispatchers.IO) { + runCatching { + feature.context = context + synchronized(features) { + features[feature::class] = feature + } + }.onFailure { + CoreLogger.xposedLog("Failed to register feature ${feature.featureKey}", it) + } + } + } + } + } + + @Suppress("UNCHECKED_CAST") + fun <T : Feature> get(featureClass: KClass<T>): T? { + return features[featureClass] as? T + } + + fun getRuleFeatures() = features.values.filterIsInstance<MessagingRuleFeature>().sortedBy { it.ruleType.ordinal } + + fun init() { + register( + 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(), + 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(), + StealthModeIndicator(), + DisablePermissionRequests(), + SessionEvents(), + DefaultVolumeControls(), + CallRecorder(), + DisableMemoriesSnapFeed(), + AccountSwitcher(), + ) + + 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 + ) { + + features.values.toList().forEach { feature -> + if (feature.loadParams and syncParam != 0) { + tryInit(feature) { + syncAction(feature) + } + } + if (feature.loadParams and asyncParam != 0) { + context.coroutineScope.launch { + tryInit(feature) { + asyncAction(feature) + } + } + } + } + } + + private fun initializeFeatures() { + //TODO: async called when all features are initiated ? + measureTimeMillis { + initFeatures( + FeatureLoadParams.INIT_SYNC, + FeatureLoadParams.INIT_ASYNC, + Feature::init, + Feature::asyncInit + ) + }.also { + context.log.verbose("feature manager init took $it ms") + } + } + + fun onActivityCreate() { + measureTimeMillis { + initFeatures( + FeatureLoadParams.ACTIVITY_CREATE_SYNC, + FeatureLoadParams.ACTIVITY_CREATE_ASYNC, + Feature::onActivityCreate, + Feature::asyncOnActivityCreate + ) + }.also { + context.log.verbose("feature manager onActivityCreate took $it ms") + } + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/Manager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/Manager.kt @@ -1,6 +0,0 @@ -package me.rhunk.snapenhance.core.manager - -interface Manager { - fun init() {} - fun onActivityCreate() {} -}- \ No newline at end of file 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 @@ -1,45 +0,0 @@ -package me.rhunk.snapenhance.core.manager.impl - -import android.content.Intent -import me.rhunk.snapenhance.common.action.EnumAction -import me.rhunk.snapenhance.core.ModContext -import me.rhunk.snapenhance.core.action.impl.BulkMessagingAction -import me.rhunk.snapenhance.core.action.impl.CleanCache -import me.rhunk.snapenhance.core.action.impl.ExportChatMessages -import me.rhunk.snapenhance.core.action.impl.ExportMemories -import me.rhunk.snapenhance.core.manager.Manager - -class ActionManager( - private val modContext: ModContext, -) : Manager { - - private val actions by lazy { - mapOf( - 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.apply { - this.context = modContext - } - }.toMap().toMutableMap() - } - - override fun init() { - } - - fun onNewIntent(intent: Intent?) { - val action = intent?.getStringExtra(EnumAction.ACTION_PARAMETER) ?: return - intent.removeExtra(EnumAction.ACTION_PARAMETER) - execute(EnumAction.entries.find { it.key == action } ?: return) - } - - fun execute(enumAction: EnumAction) { - val action = actions[enumAction] ?: return - action.run() - if (enumAction.exitOnFinish) { - modContext.forceCloseApp() - } - } -}- \ No newline at end of file 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 @@ -1,189 +0,0 @@ -package me.rhunk.snapenhance.core.manager.impl - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import me.rhunk.snapenhance.core.ModContext -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.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.* -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.DisablePermissionRequests -import me.rhunk.snapenhance.core.features.impl.tweaks.PreventMessageListAutoScroll -import me.rhunk.snapenhance.core.features.impl.tweaks.UnsaveableMessages -import me.rhunk.snapenhance.core.features.impl.ui.* -import me.rhunk.snapenhance.core.logger.CoreLogger -import me.rhunk.snapenhance.core.manager.Manager -import me.rhunk.snapenhance.core.ui.menu.MenuViewInjector -import kotlin.reflect.KClass -import kotlin.system.measureTimeMillis - -class FeatureManager( - private val context: ModContext -) : Manager { - private val features = mutableMapOf<KClass<out Feature>, Feature>() - - private fun register(vararg featureList: Feature) { - runBlocking { - featureList.forEach { feature -> - launch(Dispatchers.IO) { - runCatching { - feature.context = context - synchronized(features) { - features[feature::class] = feature - } - }.onFailure { - CoreLogger.xposedLog("Failed to register feature ${feature.featureKey}", it) - } - } - } - } - } - - @Suppress("UNCHECKED_CAST") - fun <T : Feature> get(featureClass: KClass<T>): T? { - return features[featureClass] as? T - } - - fun getRuleFeatures() = features.values.filterIsInstance<MessagingRuleFeature>().sortedBy { it.ruleType.ordinal } - - override fun init() { - register( - 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(), - 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(), - StealthModeIndicator(), - DisablePermissionRequests(), - SessionEvents(), - DefaultVolumeControls(), - CallRecorder(), - DisableMemoriesSnapFeed(), - AccountSwitcher(), - ) - - 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 - ) { - - features.values.toList().forEach { feature -> - if (feature.loadParams and syncParam != 0) { - tryInit(feature) { - syncAction(feature) - } - } - if (feature.loadParams and asyncParam != 0) { - context.coroutineScope.launch { - tryInit(feature) { - asyncAction(feature) - } - } - } - } - } - - private fun initializeFeatures() { - //TODO: async called when all features are initiated ? - measureTimeMillis { - initFeatures( - FeatureLoadParams.INIT_SYNC, - FeatureLoadParams.INIT_ASYNC, - Feature::init, - Feature::asyncInit - ) - }.also { - context.log.verbose("feature manager init took $it ms") - } - } - - override fun onActivityCreate() { - measureTimeMillis { - initFeatures( - FeatureLoadParams.ACTIVITY_CREATE_SYNC, - FeatureLoadParams.ACTIVITY_CREATE_ASYNC, - Feature::onActivityCreate, - Feature::asyncOnActivityCreate - ) - }.also { - context.log.verbose("feature manager onActivityCreate took $it ms") - } - } -}- \ No newline at end of file