commit 6492cf3b483281e7c7da37e26e39791807827d5d
parent d18dff61d3f394e9c18c92eadcacdc0b54bc02c6
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sun, 11 Jun 2023 12:25:45 +0200

feat: group chat menu

Diffstat:
Mapp/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MenuViewInjector.kt | 59++++++++++++++++++++++++++++++++++++++++++++---------------
Mapp/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/impl/FriendFeedInfoMenu.kt | 30+++++++++++++++++-------------
Mapp/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt | 13+++++++++++++
3 files changed, 74 insertions(+), 28 deletions(-)

diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MenuViewInjector.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MenuViewInjector.kt @@ -1,11 +1,11 @@ package me.rhunk.snapenhance.features.impl.ui.menus import android.annotation.SuppressLint +import android.view.Gravity import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout -import de.robv.android.xposed.XposedBridge import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature @@ -30,6 +30,12 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar context.resources.getString(context.resources.getIdentifier("new_chat", "string", Constants.SNAPCHAT_PACKAGE_NAME)) } + private fun wasInjectedView(view: View): Boolean { + if (view.getTag(Constants.VIEW_INJECTED_CODE) != null) return true + view.setTag(Constants.VIEW_INJECTED_CODE, true) + return false + } + @SuppressLint("ResourceType") override fun asyncOnActivityCreate() { friendFeedInfoMenu.context = context @@ -38,6 +44,9 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar settingMenu.context = context val actionSheetItemsContainerLayoutId = context.resources.getIdentifier("action_sheet_items_container", "id", Constants.SNAPCHAT_PACKAGE_NAME) + val actionSheetContainer = context.resources.getIdentifier("action_sheet_container", "id", Constants.SNAPCHAT_PACKAGE_NAME) + val actionMenu = context.resources.getIdentifier("action_menu", "id", Constants.SNAPCHAT_PACKAGE_NAME) + val addViewMethod = ViewGroup::class.java.getMethod( "addView", View::class.java, @@ -47,18 +56,12 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar Hooker.hook(addViewMethod, HookStage.BEFORE) { param -> val viewGroup: ViewGroup = param.thisObject() - val originalAddView: (View) -> Unit = { view: View -> - XposedBridge.invokeOriginalMethod( - addViewMethod, - viewGroup, - arrayOf( - view, - -1, - FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ) - ) + val originalAddView: (View) -> Unit = { + param.invokeOriginal(arrayOf(it, -1, + FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )) ) } @@ -72,7 +75,34 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar return@hook } - //TODO : preview group chats + //inject in group chat menus + if (viewGroup.id == actionSheetContainer && childView.id == actionMenu) { + val injectedLayout = LinearLayout(childView.context).apply { + orientation = LinearLayout.VERTICAL + gravity = Gravity.BOTTOM + layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + addView(childView) + } + + Hooker.ephemeralHook(context.classCache.conversationManager, "fetchConversation", HookStage.AFTER) { + if (wasInjectedView(injectedLayout)) return@ephemeralHook + context.feature(Messaging::class).lastFetchConversationUserUUID = null + + context.runOnUiThread { + val viewList = mutableListOf<View>() + friendFeedInfoMenu.inject(injectedLayout) { view -> + view.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 10, 0, 10) + } + viewList.add(view) + } + viewList.reversed().forEach { injectedLayout.addView(it, 0) } + } + } + + param.setArg(0, injectedLayout) + } + if (viewGroup is LinearLayout && viewGroup.id == actionSheetItemsContainerLayoutId) { val itemStringInterface by lazy { childView.javaClass.declaredFields.filter { @@ -87,7 +117,6 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar //the 3 dot button shows a menu which contains the first item as a Plain object if (viewGroup.getChildCount() == 0 && itemStringInterface != null && itemStringInterface.toString().startsWith("Plain(primaryText=$newChatString")) { - settingMenu.inject(viewGroup, originalAddView) viewGroup.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) {} diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/impl/FriendFeedInfoMenu.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/impl/FriendFeedInfoMenu.kt @@ -223,7 +223,7 @@ class FriendFeedInfoMenu : AbstractMenu() { val friendFeedMenuOptions = context.config.options(ConfigProperty.FRIEND_FEED_MENU_BUTTONS) if (friendFeedMenuOptions.none { it.value }) return - val (conversationId, focusedConversationTargetUser) = getCurrentConversationId() + val (conversationId, targetUser) = getCurrentConversationId() if (!context.config.bool(ConfigProperty.ENABLE_FRIEND_FEED_MENU_BAR)) { //preview button @@ -232,7 +232,7 @@ class FriendFeedInfoMenu : AbstractMenu() { ViewAppearanceHelper.applyTheme(this, viewModel.width) setOnClickListener { showPreview( - focusedConversationTargetUser, + targetUser, conversationId, context ) @@ -252,6 +252,14 @@ class FriendFeedInfoMenu : AbstractMenu() { } } + if (friendFeedMenuOptions["anti_auto_save"] == true) { + createToggleFeature(viewConsumer, + "friend_menu_option.anti_auto_save", + { context.feature(AntiAutoSave::class).isConversationIgnored(conversationId) }, + { context.feature(AntiAutoSave::class).setConversationIgnored(conversationId, it) } + ) + } + run { val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId ?: return@run if (friendFeedMenuOptions["auto_download_blacklist"] == true) { @@ -261,18 +269,14 @@ class FriendFeedInfoMenu : AbstractMenu() { { context.feature(AntiAutoDownload::class).setUserIgnored(userId, it) } ) } - - if (friendFeedMenuOptions["anti_auto_save"] == true) { - createToggleFeature(viewConsumer, - "friend_menu_option.anti_auto_save", - { context.feature(AntiAutoSave::class).isConversationIgnored(conversationId) }, - { context.feature(AntiAutoSave::class).setConversationIgnored(conversationId, it) } - ) - } } - viewConsumer(stealthSwitch) - viewConsumer(previewButton) + if (friendFeedMenuOptions["stealth_mode"] == true) { + viewConsumer(stealthSwitch) + } + if (friendFeedMenuOptions["conversation_info"] == true) { + viewConsumer(previewButton) + } return } @@ -356,7 +360,7 @@ class FriendFeedInfoMenu : AbstractMenu() { //user createActionButton("\uD83D\uDC64") { showPreview( - focusedConversationTargetUser, + targetUser, conversationId, viewModel.context ) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt @@ -92,6 +92,19 @@ object Hooker { }.also { unhooks.addAll(it) } } + fun ephemeralHook( + clazz: Class<*>, + methodName: String, + stage: HookStage, + hookConsumer: (HookAdapter) -> Unit + ) { + val unhooks: MutableSet<XC_MethodHook.Unhook> = HashSet() + hook(clazz, methodName, stage) { param-> + hookConsumer(param) + unhooks.forEach{ it.unhook() } + }.also { unhooks.addAll(it) } + } + fun ephemeralHookObjectMethod( clazz: Class<*>, instance: Any,