feat: group chat menu

This commit is contained in:
rhunk 2023-06-11 12:25:45 +02:00
parent d18dff61d3
commit 6492cf3b48
3 changed files with 74 additions and 28 deletions

View File

@ -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) {}

View File

@ -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
)

View File

@ -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,