mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-29 21:10:20 +02:00
feat: group chat menu
This commit is contained in:
parent
d18dff61d3
commit
6492cf3b48
@ -1,11 +1,11 @@
|
|||||||
package me.rhunk.snapenhance.features.impl.ui.menus
|
package me.rhunk.snapenhance.features.impl.ui.menus
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import de.robv.android.xposed.XposedBridge
|
|
||||||
import me.rhunk.snapenhance.Constants
|
import me.rhunk.snapenhance.Constants
|
||||||
import me.rhunk.snapenhance.config.ConfigProperty
|
import me.rhunk.snapenhance.config.ConfigProperty
|
||||||
import me.rhunk.snapenhance.features.Feature
|
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))
|
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")
|
@SuppressLint("ResourceType")
|
||||||
override fun asyncOnActivityCreate() {
|
override fun asyncOnActivityCreate() {
|
||||||
friendFeedInfoMenu.context = context
|
friendFeedInfoMenu.context = context
|
||||||
@ -38,6 +44,9 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar
|
|||||||
settingMenu.context = context
|
settingMenu.context = context
|
||||||
|
|
||||||
val actionSheetItemsContainerLayoutId = context.resources.getIdentifier("action_sheet_items_container", "id", Constants.SNAPCHAT_PACKAGE_NAME)
|
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(
|
val addViewMethod = ViewGroup::class.java.getMethod(
|
||||||
"addView",
|
"addView",
|
||||||
View::class.java,
|
View::class.java,
|
||||||
@ -47,18 +56,12 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar
|
|||||||
|
|
||||||
Hooker.hook(addViewMethod, HookStage.BEFORE) { param ->
|
Hooker.hook(addViewMethod, HookStage.BEFORE) { param ->
|
||||||
val viewGroup: ViewGroup = param.thisObject()
|
val viewGroup: ViewGroup = param.thisObject()
|
||||||
val originalAddView: (View) -> Unit = { view: View ->
|
val originalAddView: (View) -> Unit = {
|
||||||
XposedBridge.invokeOriginalMethod(
|
param.invokeOriginal(arrayOf(it, -1,
|
||||||
addViewMethod,
|
FrameLayout.LayoutParams(
|
||||||
viewGroup,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
arrayOf(
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
view,
|
))
|
||||||
-1,
|
|
||||||
FrameLayout.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +75,34 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar
|
|||||||
return@hook
|
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) {
|
if (viewGroup is LinearLayout && viewGroup.id == actionSheetItemsContainerLayoutId) {
|
||||||
val itemStringInterface by lazy {
|
val itemStringInterface by lazy {
|
||||||
childView.javaClass.declaredFields.filter {
|
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
|
//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")) {
|
if (viewGroup.getChildCount() == 0 && itemStringInterface != null && itemStringInterface.toString().startsWith("Plain(primaryText=$newChatString")) {
|
||||||
|
|
||||||
settingMenu.inject(viewGroup, originalAddView)
|
settingMenu.inject(viewGroup, originalAddView)
|
||||||
viewGroup.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener {
|
viewGroup.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener {
|
||||||
override fun onViewAttachedToWindow(v: View) {}
|
override fun onViewAttachedToWindow(v: View) {}
|
||||||
|
@ -223,7 +223,7 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
|||||||
val friendFeedMenuOptions = context.config.options(ConfigProperty.FRIEND_FEED_MENU_BUTTONS)
|
val friendFeedMenuOptions = context.config.options(ConfigProperty.FRIEND_FEED_MENU_BUTTONS)
|
||||||
if (friendFeedMenuOptions.none { it.value }) return
|
if (friendFeedMenuOptions.none { it.value }) return
|
||||||
|
|
||||||
val (conversationId, focusedConversationTargetUser) = getCurrentConversationId()
|
val (conversationId, targetUser) = getCurrentConversationId()
|
||||||
|
|
||||||
if (!context.config.bool(ConfigProperty.ENABLE_FRIEND_FEED_MENU_BAR)) {
|
if (!context.config.bool(ConfigProperty.ENABLE_FRIEND_FEED_MENU_BAR)) {
|
||||||
//preview button
|
//preview button
|
||||||
@ -232,7 +232,7 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
|||||||
ViewAppearanceHelper.applyTheme(this, viewModel.width)
|
ViewAppearanceHelper.applyTheme(this, viewModel.width)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
showPreview(
|
showPreview(
|
||||||
focusedConversationTargetUser,
|
targetUser,
|
||||||
conversationId,
|
conversationId,
|
||||||
context
|
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 {
|
run {
|
||||||
val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId ?: return@run
|
val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId ?: return@run
|
||||||
if (friendFeedMenuOptions["auto_download_blacklist"] == true) {
|
if (friendFeedMenuOptions["auto_download_blacklist"] == true) {
|
||||||
@ -261,18 +269,14 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
|||||||
{ context.feature(AntiAutoDownload::class).setUserIgnored(userId, it) }
|
{ 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)
|
if (friendFeedMenuOptions["stealth_mode"] == true) {
|
||||||
viewConsumer(previewButton)
|
viewConsumer(stealthSwitch)
|
||||||
|
}
|
||||||
|
if (friendFeedMenuOptions["conversation_info"] == true) {
|
||||||
|
viewConsumer(previewButton)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +360,7 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
|||||||
//user
|
//user
|
||||||
createActionButton("\uD83D\uDC64") {
|
createActionButton("\uD83D\uDC64") {
|
||||||
showPreview(
|
showPreview(
|
||||||
focusedConversationTargetUser,
|
targetUser,
|
||||||
conversationId,
|
conversationId,
|
||||||
viewModel.context
|
viewModel.context
|
||||||
)
|
)
|
||||||
|
@ -92,6 +92,19 @@ object Hooker {
|
|||||||
}.also { unhooks.addAll(it) }
|
}.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(
|
fun ephemeralHookObjectMethod(
|
||||||
clazz: Class<*>,
|
clazz: Class<*>,
|
||||||
instance: Any,
|
instance: Any,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user