commit fc941170294a627b5d3666da517287d82fe0c423
parent 85335dafa81e59af9a35c65a8cdcdf9463350984
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sun,  4 Jun 2023 15:02:06 +0200

perf: ui improvements

Diffstat:
Mapp/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MenuViewInjector.kt | 93+++++++++++++++++++++++++++----------------------------------------------------
Mapp/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/impl/FriendFeedInfoMenu.kt | 10+++++-----
Mapp/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/impl/OperaContextActionMenu.kt | 1+
3 files changed, 38 insertions(+), 66 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,15 +1,12 @@ package me.rhunk.snapenhance.features.impl.ui.menus import android.annotation.SuppressLint -import android.content.Context -import android.util.AttributeSet 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.VIEW_DRAWER -import me.rhunk.snapenhance.Constants.VIEW_INJECTED_CODE +import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams @@ -20,19 +17,17 @@ import me.rhunk.snapenhance.features.impl.ui.menus.impl.OperaContextActionMenu import me.rhunk.snapenhance.features.impl.ui.menus.impl.SettingsMenu import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.Hooker -import java.lang.reflect.Field import java.lang.reflect.Modifier +@SuppressLint("DiscouragedApi") class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { private val friendFeedInfoMenu = FriendFeedInfoMenu() private val operaContextActionMenu = OperaContextActionMenu() private val chatActionMenu = ChatActionMenu() private val settingMenu = SettingsMenu() - private fun wasInjectedView(view: View): Boolean { - if (view.getTag(VIEW_INJECTED_CODE) != null) return true - view.setTag(VIEW_INJECTED_CODE, true) - return false + private val newChatString by lazy { + context.resources.getString(context.resources.getIdentifier("new_chat", "string", Constants.SNAPCHAT_PACKAGE_NAME)) } @SuppressLint("ResourceType") @@ -42,6 +37,7 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar chatActionMenu.context = context settingMenu.context = context + val actionSheetItemsContainerLayoutId = context.resources.getIdentifier("action_sheet_items_container", "id", Constants.SNAPCHAT_PACKAGE_NAME) val addViewMethod = ViewGroup::class.java.getMethod( "addView", View::class.java, @@ -49,22 +45,6 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar ViewGroup.LayoutParams::class.java ) - //catch the card view instance in the action drawer - Hooker.hook( - LinearLayout::class.java.getConstructor( - Context::class.java, - AttributeSet::class.java, - Int::class.javaPrimitiveType - ), HookStage.AFTER - ) { param -> - val viewGroup: LinearLayout = param.thisObject() - val attribute: Int = param.arg(2) - if (attribute == 0) return@hook - val resourceName = viewGroup.resources.getResourceName(attribute) - if (!resourceName.endsWith("snapCardContentLayoutStyle")) return@hook - viewGroup.setTag(VIEW_DRAWER, Any()) - } - Hooker.hook(addViewMethod, HookStage.BEFORE) { param -> val viewGroup: ViewGroup = param.thisObject() val originalAddView: (View) -> Unit = { view: View -> @@ -87,52 +67,43 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar //download in chat snaps and notes from the chat action menu if (viewGroup.javaClass.name.endsWith("ActionMenuChatItemContainer")) { - if (viewGroup.parent == null || viewGroup.parent - .parent == null - ) return@hook + if (viewGroup.parent == null || viewGroup.parent.parent == null) return@hook chatActionMenu.inject(viewGroup) return@hook } //TODO : preview group chats - if (viewGroup !is LinearLayout) return@hook - if (viewGroup.getTag(VIEW_DRAWER) == null) return@hook - val itemStringInterface =childView.javaClass.declaredFields.filter { field: Field -> - !field.type.isPrimitive && Modifier.isAbstract( - field.type.modifiers - ) - } - .map { field: Field -> - try { - field.isAccessible = true - return@map field[childView] - } catch (e: IllegalAccessException) { - e.printStackTrace() - } - null + if (viewGroup is LinearLayout && viewGroup.id == actionSheetItemsContainerLayoutId) { + val itemStringInterface by lazy { + childView.javaClass.declaredFields.filter { + !it.type.isPrimitive && Modifier.isAbstract(it.type.modifiers) + }.map { + runCatching { + it.isAccessible = true + it[childView] + }.getOrNull() }.firstOrNull() + } - //the 3 dot button shows a menu which contains the first item as a Plain object - //FIXME: better way to detect the 3 dot button - if (viewGroup.getChildCount() == 0 && itemStringInterface != null && itemStringInterface.toString().startsWith("Plain(primaryText=")) { - if (wasInjectedView(viewGroup)) return@hook + //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) {} - override fun onViewDetachedFromWindow(v: View) { - context.config.writeConfig() - } - }) - return@hook - } - if (context.feature(Messaging::class).lastFetchConversationUserUUID == null) return@hook + settingMenu.inject(viewGroup, originalAddView) + viewGroup.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) {} + override fun onViewDetachedFromWindow(v: View) { + context.config.writeConfig() + } + }) + return@hook + } + if (context.feature(Messaging::class).lastFetchConversationUserUUID == null) return@hook - //filter by the slot index - if (viewGroup.getChildCount() != context.config.int(ConfigProperty.MENU_SLOT_ID)) return@hook + //filter by the slot index + if (viewGroup.getChildCount() != context.config.int(ConfigProperty.MENU_SLOT_ID)) return@hook + friendFeedInfoMenu.inject(viewGroup, originalAddView) + } - friendFeedInfoMenu.inject(viewGroup, originalAddView) - childView.setTag(VIEW_DRAWER, null) } } 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 @@ -45,7 +45,7 @@ class FriendFeedInfoMenu : AbstractMenu() { return SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH).format(Date(timestamp)) } - fun showProfileInfo(profile: FriendInfo) { + private fun showProfileInfo(profile: FriendInfo) { var icon: Drawable? = null try { if (profile.bitmojiSelfieId != null && profile.bitmojiAvatarId != null) { @@ -90,14 +90,14 @@ class FriendFeedInfoMenu : AbstractMenu() { } } - fun showPreview(userId: String?, conversationId: String, androidCtx: Context?) { + private fun showPreview(userId: String?, conversationId: String, androidCtx: Context?) { //query message val messages: List<ConversationMessage>? = context.database.getMessagesFromConversationId( conversationId, context.config.int(ConfigProperty.MESSAGE_PREVIEW_LENGTH) )?.reversed() - if (messages == null || messages.isEmpty()) { + if (messages.isNullOrEmpty()) { Toast.makeText(androidCtx, "No messages found", Toast.LENGTH_SHORT).show() return } @@ -136,8 +136,8 @@ class FriendFeedInfoMenu : AbstractMenu() { val targetPerson: FriendInfo? = if (userId == null) null else participants[userId] - targetPerson?.let { - val timeSecondDiff = ((it.streakExpirationTimestamp - System.currentTimeMillis()) / 1000 / 60).toInt() + targetPerson?.streakExpirationTimestamp?.takeIf { it > 0 }?.let { + val timeSecondDiff = ((it - System.currentTimeMillis()) / 1000 / 60).toInt() messageBuilder.append("\n\n") .append("\uD83D\uDD25 ") //fire emoji .append(context.translation.get("conversation_preview.streak_expiration").format( diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/impl/OperaContextActionMenu.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/impl/OperaContextActionMenu.kt @@ -13,6 +13,7 @@ import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.features.impl.ui.menus.AbstractMenu import me.rhunk.snapenhance.features.impl.ui.menus.ViewAppearanceHelper.applyTheme +@SuppressLint("DiscouragedApi") class OperaContextActionMenu : AbstractMenu() { private val contextCardsScrollView by lazy { context.resources.getIdentifier("context_cards_scroll_view", "id", Constants.SNAPCHAT_PACKAGE_NAME)