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