commit a341801be37427ff60e663b2d20ed10c91a50b32
parent 2c16f41fd69c12b713b3bc1cfa3a5426b798b4e1
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sun, 24 Sep 2023 00:22:03 +0200

feat: more profile information
- refactor DatabaseAccess
- refactor wasInjectedView to ViewTagState
- refactor applyTheme ktx

Diffstat:
Mcore/src/main/assets/lang/en_US.json | 22++++++++++++++++++++--
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/database/DatabaseAccess.kt | 143+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/database/objects/FriendInfo.kt | 10+++++++++-
Mcore/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt | 18++++++++++++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt | 4++++
Acore/src/main/kotlin/me/rhunk/snapenhance/ui/ViewTagState.kt | 21+++++++++++++++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt | 18++++++------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt | 35+++++++++++++++++++++++++----------
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt | 7++++++-
Mcore/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt | 4++--
11 files changed, 187 insertions(+), 97 deletions(-)

diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json @@ -672,10 +672,28 @@ "profile_info": { "title": "Profile Info", - "username": "Username", + "first_created_username": "First Created Username", + "mutable_username": "Mutable Username", "display_name": "Display Name", "added_date": "Added Date", - "birthday": "Birthday : {month} {day}" + "birthday": "Birthday : {month} {day}", + "friendship": "Friendship", + "add_source": "Add Source", + "snapchat_plus": "Snapchat Plus", + "snapchat_plus_state": { + "subscribed": "Subscribed", + "not_subscribed": "Not Subscribed" + }, + "friendship_link_type": { + "mutual": "Mutual", + "outgoing": "Outgoing", + "blocked": "Blocked", + "deleted": "Deleted", + "following": "Following", + "suggested": "Suggested", + "incoming": "Incoming", + "incoming_follower": "Incoming Follower" + } }, "chat_export": { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/database/DatabaseAccess.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/database/DatabaseAccess.kt @@ -9,6 +9,7 @@ import me.rhunk.snapenhance.core.database.objects.FriendFeedEntry import me.rhunk.snapenhance.core.database.objects.FriendInfo import me.rhunk.snapenhance.core.database.objects.StoryEntry import me.rhunk.snapenhance.core.database.objects.UserConversationLink +import me.rhunk.snapenhance.core.util.ktx.getStringOrNull import me.rhunk.snapenhance.manager.Manager import java.io.File @@ -66,19 +67,16 @@ class DatabaseAccess(private val context: ModContext) : Manager { table: String, where: String, args: Array<String> - ): T? { - val cursor = database.rawQuery("SELECT * FROM $table WHERE $where", args) - if (!cursor.moveToFirst()) { - cursor.close() + ): T? = database.rawQuery("SELECT * FROM $table WHERE $where", args).use { + if (!it.moveToFirst()) { return null } try { - obj.write(cursor) + obj.write(it) } catch (e: Throwable) { context.log.error("Failed to read database object", e) } - cursor.close() - return obj + obj } fun getFeedEntryByUserId(userId: String): FriendFeedEntry? { @@ -95,18 +93,14 @@ class DatabaseAccess(private val context: ModContext) : Manager { val myUserId by lazy { safeDatabaseOperation(openArroyo()) { arroyoDatabase: SQLiteDatabase -> - val cursor = arroyoDatabase.rawQuery(buildString { + arroyoDatabase.rawQuery(buildString { append("SELECT * FROM required_values WHERE key = 'USERID'") - }, null) - - if (!cursor.moveToFirst()) { - cursor.close() - return@safeDatabaseOperation null + }, null).use { query -> + if (!query.moveToFirst()) { + return@safeDatabaseOperation null + } + query.getStringOrNull("value")!! } - - val userId = cursor.getString(cursor.getColumnIndex("value")) - cursor.close() - userId }!! } @@ -136,20 +130,20 @@ class DatabaseAccess(private val context: ModContext) : Manager { fun getFeedEntries(limit: Int): List<FriendFeedEntry> { return safeDatabaseOperation(openMain()) { database -> - val cursor = database.rawQuery( + database.rawQuery( "SELECT * FROM FriendsFeedView ORDER BY _id LIMIT ?", arrayOf(limit.toString()) - ) - val list = mutableListOf<FriendFeedEntry>() - while (cursor.moveToNext()) { - val friendFeedEntry = FriendFeedEntry() - try { - friendFeedEntry.write(cursor) - } catch (_: Throwable) {} - list.add(friendFeedEntry) + ).use { query -> + val list = mutableListOf<FriendFeedEntry>() + while (query.moveToNext()) { + val friendFeedEntry = FriendFeedEntry() + try { + friendFeedEntry.write(query) + } catch (_: Throwable) {} + list.add(friendFeedEntry) + } + list } - cursor.close() - list } ?: emptyList() } @@ -166,18 +160,16 @@ class DatabaseAccess(private val context: ModContext) : Manager { } fun getConversationType(conversationId: String): Int? { - return safeDatabaseOperation(openArroyo()) { - val cursor = it.rawQuery( + return safeDatabaseOperation(openArroyo()) { database -> + database.rawQuery( "SELECT * FROM user_conversation WHERE client_conversation_id = ?", arrayOf(conversationId) - ) - if (!cursor.moveToFirst()) { - cursor.close() - return@safeDatabaseOperation null + ).use { query -> + if (!query.moveToFirst()) { + return@safeDatabaseOperation null + } + query.getInt(query.getColumnIndex("conversation_type")) } - val type = cursor.getInt(cursor.getColumnIndex("conversation_type")) - cursor.close() - type } } @@ -195,16 +187,19 @@ class DatabaseAccess(private val context: ModContext) : Manager { fun getDMOtherParticipant(conversationId: String): String? { return safeDatabaseOperation(openArroyo()) { cursor -> - val query = cursor.rawQuery( + cursor.rawQuery( "SELECT * FROM user_conversation WHERE client_conversation_id = ? AND conversation_type = 0", arrayOf(conversationId) - ) - val participants = mutableListOf<String>() - while (query.moveToNext()) { - participants.add(query.getString(query.getColumnIndex("user_id"))) + ).use { query -> + val participants = mutableListOf<String>() + if (!query.moveToFirst()) { + return@safeDatabaseOperation null + } + do { + participants.add(query.getString(query.getColumnIndex("user_id"))) + } while (query.moveToNext()) + participants.firstOrNull { it != myUserId } } - query.close() - participants.firstOrNull { it != myUserId } } } @@ -217,20 +212,19 @@ class DatabaseAccess(private val context: ModContext) : Manager { fun getConversationParticipants(conversationId: String): List<String>? { return safeDatabaseOperation(openArroyo()) { arroyoDatabase: SQLiteDatabase -> - val cursor = arroyoDatabase.rawQuery( + arroyoDatabase.rawQuery( "SELECT * FROM user_conversation WHERE client_conversation_id = ?", arrayOf(conversationId) - ) - if (!cursor.moveToFirst()) { - cursor.close() - return@safeDatabaseOperation emptyList() + ).use { + if (!it.moveToFirst()) { + return@safeDatabaseOperation null + } + val participants = mutableListOf<String>() + do { + participants.add(it.getString(it.getColumnIndex("user_id"))) + } while (it.moveToNext()) + participants } - val participants = mutableListOf<String>() - do { - participants.add(cursor.getString(cursor.getColumnIndex("user_id"))) - } while (cursor.moveToNext()) - cursor.close() - participants } } @@ -239,22 +233,35 @@ class DatabaseAccess(private val context: ModContext) : Manager { limit: Int ): List<ConversationMessage>? { return safeDatabaseOperation(openArroyo()) { arroyoDatabase: SQLiteDatabase -> - val cursor = arroyoDatabase.rawQuery( + arroyoDatabase.rawQuery( "SELECT * FROM conversation_message WHERE client_conversation_id = ? ORDER BY creation_timestamp DESC LIMIT ?", arrayOf(conversationId, limit.toString()) - ) - if (!cursor.moveToFirst()) { - cursor.close() - return@safeDatabaseOperation emptyList() + ).use { query -> + if (!query.moveToFirst()) { + return@safeDatabaseOperation null + } + val messages = mutableListOf<ConversationMessage>() + do { + val message = ConversationMessage() + message.write(query) + messages.add(message) + } while (query.moveToNext()) + messages + } + } + } + + fun getAddSource(userId: String): String? { + return safeDatabaseOperation(openMain()) { database -> + database.rawQuery( + "SELECT addSource FROM FriendWhoAddedMe WHERE userId = ?", + arrayOf(userId) + ).use { + if (!it.moveToFirst()) { + return@safeDatabaseOperation null + } + it.getStringOrNull("addSource") } - val messages = mutableListOf<ConversationMessage>() - do { - val message = ConversationMessage() - message.write(cursor) - messages.add(message) - } while (cursor.moveToNext()) - cursor.close() - messages } } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/database/objects/FriendInfo.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/database/objects/FriendInfo.kt @@ -30,8 +30,13 @@ data class FriendInfo( var reverseBestFriendRanking: Int = 0, var isPinnedBestFriend: Int = 0, var plusBadgeVisibility: Int = 0, - var usernameForSorting: String? = null + var usernameForSorting: String? = null, + var friendLinkType: Int = 0, + var postViewEmoji: String? = null, ) : DatabaseObject, SerializableDataObject() { + val mutableUsername get() = username?.split("|")?.last() + val firstCreatedUsername get() = username?.split("|")?.first() + @SuppressLint("Range") override fun write(cursor: Cursor) { with(cursor) { @@ -55,10 +60,13 @@ data class FriendInfo( streakExpirationTimestamp = getLong("streakExpiration") reverseBestFriendRanking = getInteger("reverseBestFriendRanking") usernameForSorting = getStringOrNull("usernameForSorting") + friendLinkType = getInteger("friendLinkType") + postViewEmoji = getStringOrNull("postViewEmoji") if (getColumnIndex("isPinnedBestFriend") != -1) isPinnedBestFriend = getInteger("isPinnedBestFriend") if (getColumnIndex("plusBadgeVisibility") != -1) plusBadgeVisibility = getInteger("plusBadgeVisibility") } } + } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt @@ -133,3 +133,20 @@ enum class MetricsMessageType { enum class MediaReferenceType { UNASSIGNED, OVERLAY, IMAGE, VIDEO, ASSET_BUNDLE, AUDIO, ANIMATED_IMAGE, FONT, WEB_VIEW_CONTENT, VIDEO_NO_AUDIO } + +enum class FriendLinkType(val value: Int, val shortName: String) { + MUTUAL(0, "mutual"), + OUTGOING(1, "outgoing"), + BLOCKED(2, "blocked"), + DELETED(3, "deleted"), + FOLLOWING(4, "following"), + SUGGESTED(5, "suggested"), + INCOMING(6, "incoming"), + INCOMING_FOLLOWER(7, "incoming_follower"); + + companion object { + fun fromValue(value: Int): FriendLinkType { + return values().firstOrNull { it.value == value } ?: MUTUAL + } + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt @@ -15,6 +15,10 @@ import android.widget.Switch import android.widget.TextView import me.rhunk.snapenhance.Constants +fun View.applyTheme(componentWidth: Int? = null, hasRadius: Boolean = false, isAmoled: Boolean = true) { + ViewAppearanceHelper.applyTheme(this, componentWidth, hasRadius, isAmoled) +} + object ViewAppearanceHelper { @SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi", "ClickableViewAccessibility" diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/ViewTagState.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ViewTagState.kt @@ -0,0 +1,20 @@ +package me.rhunk.snapenhance.ui + +import android.view.View +import kotlin.random.Random + +class ViewTagState { + private val tag = Random.nextInt(0x7000000, 0x7FFFFFFF) + + operator fun get(view: View) = hasState(view) + + private fun hasState(view: View): Boolean { + if (view.getTag(tag) != null) return true + view.setTag(tag, true) + return false + } + + fun removeState(view: View) { + view.setTag(tag, null) + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt @@ -2,7 +2,7 @@ package me.rhunk.snapenhance.ui.menu import me.rhunk.snapenhance.ModContext -abstract class AbstractMenu() { +abstract class AbstractMenu { lateinit var context: ModContext open fun init() {} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt @@ -12,23 +12,18 @@ import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.features.impl.Messaging import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.features.impl.spying.MessageLogger -import me.rhunk.snapenhance.ui.ViewAppearanceHelper +import me.rhunk.snapenhance.ui.ViewTagState +import me.rhunk.snapenhance.ui.applyTheme import me.rhunk.snapenhance.ui.menu.AbstractMenu class ChatActionMenu : AbstractMenu() { - private val viewInjectedTag = 0x7FFFFF02 - - private fun wasInjectedView(view: View): Boolean { - if (view.getTag(viewInjectedTag) != null) return true - view.setTag(viewInjectedTag, true) - return false - } + private val viewTagState = ViewTagState() @SuppressLint("SetTextI18n", "DiscouragedApi") fun inject(viewGroup: ViewGroup) { val parent = viewGroup.parent.parent as ViewGroup - if (wasInjectedView(parent)) return + if (viewTagState[parent]) return //close the action menu using a touch event val closeActionMenu = { viewGroup.dispatchTouchEvent( @@ -73,7 +68,7 @@ class ChatActionMenu : AbstractMenu() { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ).apply { - ViewAppearanceHelper.applyTheme(this@layout, parent.width, true) + applyTheme(parent.width, true) setMargins(chatActionMenuItemMargin, 0, chatActionMenuItemMargin, defaultGap) } } @@ -91,9 +86,8 @@ class ChatActionMenu : AbstractMenu() { }) } - ViewAppearanceHelper.applyTheme(button, parent.width, true) - with(button) { + applyTheme(parent.width, true) layoutParams = MarginLayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt @@ -16,12 +16,14 @@ import me.rhunk.snapenhance.core.database.objects.FriendInfo import me.rhunk.snapenhance.core.database.objects.UserConversationLink import me.rhunk.snapenhance.core.util.snap.BitmojiSelfie import me.rhunk.snapenhance.data.ContentType +import me.rhunk.snapenhance.data.FriendLinkType import me.rhunk.snapenhance.features.MessagingRuleFeature import me.rhunk.snapenhance.features.impl.Messaging import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.features.impl.spying.StealthMode import me.rhunk.snapenhance.features.impl.tweaks.AutoSave import me.rhunk.snapenhance.ui.ViewAppearanceHelper +import me.rhunk.snapenhance.ui.applyTheme import me.rhunk.snapenhance.ui.menu.AbstractMenu import java.net.HttpURLConnection import java.net.URL @@ -59,6 +61,8 @@ class FriendFeedInfoMenu : AbstractMenu() { context.log.error("Error loading bitmoji selfie", e) } val finalIcon = icon + val translation = context.translation.getCategory("profile_info") + context.runOnUiThread { val addedTimestamp: Long = profile.addedTimestamp.coerceAtLeast(profile.reverseAddedTimestamp) val builder = ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity) @@ -67,11 +71,13 @@ class FriendFeedInfoMenu : AbstractMenu() { val birthday = Calendar.getInstance() birthday[Calendar.MONTH] = (profile.birthday shr 32).toInt() - 1 - val message: String = """ - ${context.translation["profile_info.username"]}: ${profile.username} - ${context.translation["profile_info.display_name"]}: ${profile.displayName} - ${context.translation["profile_info.added_date"]}: ${formatDate(addedTimestamp)} - ${birthday.getDisplayName( + + builder.setMessage(mapOf( + translation["first_created_username"] to profile.firstCreatedUsername, + translation["mutable_username"] to profile.mutableUsername, + translation["display_name"] to profile.displayName, + translation["added_date"] to formatDate(addedTimestamp), + null to birthday.getDisplayName( Calendar.MONTH, Calendar.LONG, context.translation.loadedLocale @@ -79,9 +85,18 @@ class FriendFeedInfoMenu : AbstractMenu() { context.translation.format("profile_info.birthday", "month" to it, "day" to profile.birthday.toInt().toString()) - }} - """.trimIndent() - builder.setMessage(message) + }, + translation["friendship"] to run { + translation.getCategory("friendship_link_type")[FriendLinkType.fromValue(profile.friendLinkType).shortName] + }, + translation["add_source"] to context.database.getAddSource(profile.userId!!)?.takeIf { it.isNotEmpty() }, + translation["snapchat_plus"] to run { + translation.getCategory("snapchat_plus_state")[if (profile.postViewEmoji != null) "subscribed" else "not_subscribed"] + } + ).filterValues { it != null }.map { + line -> "${line.key?.let { "$it: " } ?: ""}${line.value}" + }.joinToString("\n")) + builder.setPositiveButton( "OK" ) { dialog: DialogInterface, _: Int -> dialog.dismiss() } @@ -198,7 +213,7 @@ class FriendFeedInfoMenu : AbstractMenu() { val switch = Switch(context.androidContext) switch.text = context.translation[text] switch.isChecked = isChecked() - ViewAppearanceHelper.applyTheme(switch) + switch.applyTheme() switch.setOnCheckedChangeListener { _: CompoundButton?, checked: Boolean -> toggle(checked) } @@ -218,7 +233,7 @@ class FriendFeedInfoMenu : AbstractMenu() { val previewButton = Button(viewModel.context).apply { text = modContext.translation["friend_menu_option.preview"] - ViewAppearanceHelper.applyTheme(this, viewModel.width) + applyTheme(viewModel.width) setOnClickListener { showPreview( targetUser, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt @@ -11,10 +11,13 @@ import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.features.impl.Messaging +import me.rhunk.snapenhance.ui.ViewTagState import java.lang.reflect.Modifier @SuppressLint("DiscouragedApi") class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { + private val viewTagState = ViewTagState() + private val friendFeedInfoMenu = FriendFeedInfoMenu() private val operaContextActionMenu = OperaContextActionMenu() private val chatActionMenu = ChatActionMenu() @@ -114,15 +117,17 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar viewGroup.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) {} override fun onViewDetachedFromWindow(v: View) { - //context.config.writeConfig() + viewTagState.removeState(viewGroup) } }) + viewTagState[viewGroup] return@subscribe } if (messaging.lastFetchConversationUUID == null || messaging.lastFetchConversationUserUUID == null) return@subscribe //filter by the slot index if (viewGroup.getChildCount() != context.config.userInterface.friendFeedMenuPosition.get()) return@subscribe + if (viewTagState[viewGroup]) return@subscribe friendFeedInfoMenu.inject(viewGroup, originalAddView) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt @@ -9,7 +9,7 @@ import android.widget.LinearLayout import android.widget.ScrollView import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader -import me.rhunk.snapenhance.ui.ViewAppearanceHelper.applyTheme +import me.rhunk.snapenhance.ui.applyTheme import me.rhunk.snapenhance.ui.menu.AbstractMenu @SuppressLint("DiscouragedApi") @@ -71,7 +71,7 @@ class OperaContextActionMenu : AbstractMenu() { val button = Button(childView.getContext()) button.text = context.translation["opera_context_menu.download"] button.setOnClickListener { context.feature(MediaDownloader::class).downloadLastOperaMediaAsync() } - applyTheme(button, isAmoled = false) + button.applyTheme(isAmoled = false) linearLayout.addView(button) (childView as ViewGroup).addView(linearLayout, 0) } catch (e: Throwable) {