perf: ui improvements
This commit is contained in:
@@ -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
|
||||
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
|
||||
friendFeedInfoMenu.inject(viewGroup, originalAddView)
|
||||
}
|
||||
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
|
||||
|
||||
friendFeedInfoMenu.inject(viewGroup, originalAddView)
|
||||
childView.setTag(VIEW_DRAWER, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user