mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 13:47:47 +02:00
fix(e2ee): bind view foreground
- fix MessagingRuleType npe - fix ProtoReader exception
This commit is contained in:
@ -159,7 +159,7 @@ class ModDatabase(
|
|||||||
val rules = mutableListOf<MessagingRuleType>()
|
val rules = mutableListOf<MessagingRuleType>()
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
runCatching {
|
runCatching {
|
||||||
rules.add(MessagingRuleType.getByName(cursor.getStringOrNull("type")!!))
|
rules.add(MessagingRuleType.getByName(cursor.getStringOrNull("type")!!) ?: return@runCatching)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
context.log.error("Failed to parse rule", it)
|
context.log.error("Failed to parse rule", it)
|
||||||
}
|
}
|
||||||
|
@ -520,13 +520,19 @@
|
|||||||
"name": "No Friend Score Delay",
|
"name": "No Friend Score Delay",
|
||||||
"description": "Removes the delay when viewing a Friends Score"
|
"description": "Removes the delay when viewing a Friends Score"
|
||||||
},
|
},
|
||||||
"e2e_encryption": {
|
"e2ee": {
|
||||||
"name": "End-To-End Encryption",
|
"name": "End-To-End Encryption",
|
||||||
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!"
|
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!",
|
||||||
},
|
"properties": {
|
||||||
"encrypted_message_indicator": {
|
"encrypted_message_indicator": {
|
||||||
"name": "Encrypted Message Indicator",
|
"name": "Encrypted Message Indicator",
|
||||||
"description": "Adds a \uD83D\uDD12 emoji next to encrypted messages"
|
"description": "Adds a \uD83D\uDD12 emoji next to encrypted messages"
|
||||||
|
},
|
||||||
|
"force_message_encryption": {
|
||||||
|
"name": "Force Message Encryption",
|
||||||
|
"description": "Prevents sending encrypted messages to people who don't have E2E Encryption enabled only when multiple conversations are selected"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"add_friend_source_spoof": {
|
"add_friend_source_spoof": {
|
||||||
"name": "Add Friend Source Spoof",
|
"name": "Add Friend Source Spoof",
|
||||||
|
@ -134,7 +134,7 @@ class BridgeClient(
|
|||||||
fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends)
|
fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends)
|
||||||
|
|
||||||
fun getRules(targetUuid: String): List<MessagingRuleType> {
|
fun getRules(targetUuid: String): List<MessagingRuleType> {
|
||||||
return service.getRules(targetUuid).map { MessagingRuleType.getByName(it) }
|
return service.getRules(targetUuid).mapNotNull { MessagingRuleType.getByName(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRuleIds(ruleType: MessagingRuleType): List<String> {
|
fun getRuleIds(ruleType: MessagingRuleType): List<String> {
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package me.rhunk.snapenhance.core.config.impl
|
||||||
|
|
||||||
|
import me.rhunk.snapenhance.core.config.ConfigContainer
|
||||||
|
|
||||||
|
class E2EEConfig : ConfigContainer(hasGlobalState = true) {
|
||||||
|
val encryptedMessageIndicator = boolean("encrypted_message_indicator")
|
||||||
|
val forceMessageEncryption = boolean("force_message_encryption")
|
||||||
|
}
|
@ -12,8 +12,7 @@ class Experimental : ConfigContainer() {
|
|||||||
val meoPasscodeBypass = boolean("meo_passcode_bypass")
|
val meoPasscodeBypass = boolean("meo_passcode_bypass")
|
||||||
val unlimitedMultiSnap = boolean("unlimited_multi_snap") { addNotices(FeatureNotice.BAN_RISK)}
|
val unlimitedMultiSnap = boolean("unlimited_multi_snap") { addNotices(FeatureNotice.BAN_RISK)}
|
||||||
val noFriendScoreDelay = boolean("no_friend_score_delay")
|
val noFriendScoreDelay = boolean("no_friend_score_delay")
|
||||||
val useE2EEncryption = boolean("e2e_encryption")
|
val e2eEncryption = container("e2ee", E2EEConfig())
|
||||||
val encryptedMessageIndicator = boolean("encrypted_message_indicator") { addNotices(FeatureNotice.UNSTABLE) }
|
|
||||||
val hiddenSnapchatPlusFeatures = boolean("hidden_snapchat_plus_features") { addNotices(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE) }
|
val hiddenSnapchatPlusFeatures = boolean("hidden_snapchat_plus_features") { addNotices(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE) }
|
||||||
val addFriendSourceSpoof = unique("add_friend_source_spoof",
|
val addFriendSourceSpoof = unique("add_friend_source_spoof",
|
||||||
"added_by_username",
|
"added_by_username",
|
||||||
|
@ -76,7 +76,9 @@ class EventDispatcher(
|
|||||||
interactionType = interactionType,
|
interactionType = interactionType,
|
||||||
conversationId = conversationId,
|
conversationId = conversationId,
|
||||||
messageId = messageId
|
messageId = messageId
|
||||||
)
|
).apply {
|
||||||
|
adapter = param
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
postHookEvent()
|
postHookEvent()
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ abstract class AbstractHookEvent : Event() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun invokeOriginal() {
|
fun invokeOriginal() {
|
||||||
canceled = true
|
|
||||||
invokeLater()
|
invokeLater()
|
||||||
adapter.invokeOriginal()
|
adapter.invokeOriginal()
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ enum class MessagingRuleType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getByName(name: String) = values().first { it.key == name }
|
fun getByName(name: String) = values().firstOrNull { it.key == name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,10 +32,10 @@ class ProtoReader(private val buffer: ByteArray) {
|
|||||||
|
|
||||||
private fun read() {
|
private fun read() {
|
||||||
while (offset < buffer.size) {
|
while (offset < buffer.size) {
|
||||||
val tag = readVarInt().toInt()
|
|
||||||
val id = tag ushr 3
|
|
||||||
val type = WireType.fromValue(tag and 0x7) ?: break
|
|
||||||
try {
|
try {
|
||||||
|
val tag = readVarInt().toInt()
|
||||||
|
val id = tag ushr 3
|
||||||
|
val type = WireType.fromValue(tag and 0x7) ?: break
|
||||||
val value = when (type) {
|
val value = when (type) {
|
||||||
WireType.VARINT -> readVarInt()
|
WireType.VARINT -> readVarInt()
|
||||||
WireType.FIXED64 -> {
|
WireType.FIXED64 -> {
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package me.rhunk.snapenhance.features.impl.experiments
|
package me.rhunk.snapenhance.features.impl.experiments
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.graphics.drawable.ShapeDrawable
|
||||||
|
import android.graphics.drawable.shapes.Shape
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroup.LayoutParams
|
import android.view.ViewGroup.LayoutParams
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
|
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
|
||||||
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
|
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
|
||||||
@ -29,6 +32,8 @@ import me.rhunk.snapenhance.features.impl.Messaging
|
|||||||
import me.rhunk.snapenhance.hook.HookStage
|
import me.rhunk.snapenhance.hook.HookStage
|
||||||
import me.rhunk.snapenhance.hook.hookConstructor
|
import me.rhunk.snapenhance.hook.hookConstructor
|
||||||
import me.rhunk.snapenhance.ui.ViewAppearanceHelper
|
import me.rhunk.snapenhance.ui.ViewAppearanceHelper
|
||||||
|
import me.rhunk.snapenhance.ui.addForegroundDrawable
|
||||||
|
import me.rhunk.snapenhance.ui.removeForegroundDrawable
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -37,7 +42,7 @@ class EndToEndEncryption : MessagingRuleFeature(
|
|||||||
MessagingRuleType.E2E_ENCRYPTION,
|
MessagingRuleType.E2E_ENCRYPTION,
|
||||||
loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_SYNC or FeatureLoadParams.INIT_ASYNC
|
loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_SYNC or FeatureLoadParams.INIT_ASYNC
|
||||||
) {
|
) {
|
||||||
private val isEnabled get() = context.config.experimental.useE2EEncryption.get()
|
private val isEnabled get() = context.config.experimental.e2eEncryption.globalState == true
|
||||||
private val e2eeInterface by lazy { context.bridgeClient.getE2eeInterface() }
|
private val e2eeInterface by lazy { context.bridgeClient.getE2eeInterface() }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -185,13 +190,11 @@ class EndToEndEncryption : MessagingRuleFeature(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val encryptedMessageIndicator by context.config.experimental.encryptedMessageIndicator
|
val encryptedMessageIndicator by context.config.experimental.e2eEncryption.encryptedMessageIndicator
|
||||||
val chatMessageContentContainerId = context.resources.getIdentifier("chat_message_content_container", "id", context.androidContext.packageName)
|
|
||||||
|
|
||||||
// hook view binder to add special buttons
|
// hook view binder to add special buttons
|
||||||
val receivePublicKeyTag = Random.nextLong().toString(16)
|
val receivePublicKeyTag = Random.nextLong().toString(16)
|
||||||
val receiveSecretTag = Random.nextLong().toString(16)
|
val receiveSecretTag = Random.nextLong().toString(16)
|
||||||
val encryptedMessageTag = Random.nextLong().toString(16)
|
|
||||||
|
|
||||||
context.event.subscribe(BindViewEvent::class) { event ->
|
context.event.subscribe(BindViewEvent::class) { event ->
|
||||||
event.chatMessage { conversationId, messageId ->
|
event.chatMessage { conversationId, messageId ->
|
||||||
@ -206,28 +209,15 @@ class EndToEndEncryption : MessagingRuleFeature(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (encryptedMessageIndicator) {
|
if (encryptedMessageIndicator) {
|
||||||
viewGroup.findViewWithTag<ViewGroup>(encryptedMessageTag)?.also {
|
viewGroup.removeForegroundDrawable("encryptedMessage")
|
||||||
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage
|
|
||||||
it.removeView(chatMessageContentContainer)
|
|
||||||
viewGroup.removeView(it)
|
|
||||||
viewGroup.addView(chatMessageContentContainer, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encryptedMessages.contains(messageId.toLong())) {
|
if (encryptedMessages.contains(messageId.toLong())) {
|
||||||
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage
|
viewGroup.addForegroundDrawable("encryptedMessage", ShapeDrawable(object: Shape() {
|
||||||
viewGroup.removeView(chatMessageContentContainer)
|
override fun draw(canvas: Canvas, paint: Paint) {
|
||||||
|
paint.textSize = 20f
|
||||||
viewGroup.addView(RelativeLayout(viewGroup.context).apply {
|
canvas.drawText("\uD83D\uDD12", 0f, canvas.height / 2f, paint)
|
||||||
tag = encryptedMessageTag
|
}
|
||||||
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
}))
|
||||||
addView(chatMessageContentContainer)
|
|
||||||
addView(TextView(viewGroup.context).apply {
|
|
||||||
text = "\uD83D\uDD12"
|
|
||||||
textAlignment = View.TEXT_ALIGNMENT_TEXT_END
|
|
||||||
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
||||||
setPadding(20, 0, 20, 0)
|
|
||||||
})
|
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,13 +345,25 @@ class EndToEndEncryption : MessagingRuleFeature(
|
|||||||
|
|
||||||
override fun asyncInit() {
|
override fun asyncInit() {
|
||||||
if (!isEnabled) return
|
if (!isEnabled) return
|
||||||
// trick to disable fidelius encryption
|
val forceMessageEncryption by context.config.experimental.e2eEncryption.forceMessageEncryption
|
||||||
context.event.subscribe(SendMessageWithContentEvent::class) { param ->
|
|
||||||
val messageContent = param.messageContent
|
|
||||||
val destinations = param.destinations
|
|
||||||
if (destinations.conversations.none { getState(it.toString()) }) return@subscribe
|
|
||||||
|
|
||||||
param.addInvokeLater {
|
// trick to disable fidelius encryption
|
||||||
|
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
|
||||||
|
val messageContent = event.messageContent
|
||||||
|
val destinations = event.destinations
|
||||||
|
|
||||||
|
val e2eeConversations = destinations.conversations.filter { getState(it.toString()) }
|
||||||
|
|
||||||
|
if (e2eeConversations.isEmpty()) return@subscribe
|
||||||
|
|
||||||
|
if (e2eeConversations.size != destinations.conversations.size) {
|
||||||
|
if (!forceMessageEncryption) return@subscribe
|
||||||
|
context.longToast("You can't send encrypted content to both encrypted and unencrypted conversations!")
|
||||||
|
event.canceled = true
|
||||||
|
return@subscribe
|
||||||
|
}
|
||||||
|
|
||||||
|
event.addInvokeLater {
|
||||||
if (messageContent.contentType == ContentType.SNAP) {
|
if (messageContent.contentType == ContentType.SNAP) {
|
||||||
messageContent.contentType = ContentType.EXTERNAL_MEDIA
|
messageContent.contentType = ContentType.EXTERNAL_MEDIA
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package me.rhunk.snapenhance.features.impl.spying
|
package me.rhunk.snapenhance.features.impl.spying
|
||||||
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.drawable.ShapeDrawable
|
||||||
|
import android.graphics.drawable.shapes.Shape
|
||||||
import android.os.DeadObjectException
|
import android.os.DeadObjectException
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
@ -12,6 +15,8 @@ import me.rhunk.snapenhance.features.Feature
|
|||||||
import me.rhunk.snapenhance.features.FeatureLoadParams
|
import me.rhunk.snapenhance.features.FeatureLoadParams
|
||||||
import me.rhunk.snapenhance.hook.HookStage
|
import me.rhunk.snapenhance.hook.HookStage
|
||||||
import me.rhunk.snapenhance.hook.Hooker
|
import me.rhunk.snapenhance.hook.Hooker
|
||||||
|
import me.rhunk.snapenhance.ui.addForegroundDrawable
|
||||||
|
import me.rhunk.snapenhance.ui.removeForegroundDrawable
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
import kotlin.time.measureTime
|
import kotlin.time.measureTime
|
||||||
@ -155,14 +160,18 @@ class MessageLogger : Feature("MessageLogger",
|
|||||||
|
|
||||||
context.event.subscribe(BindViewEvent::class) { event ->
|
context.event.subscribe(BindViewEvent::class) { event ->
|
||||||
event.chatMessage { conversationId, messageId ->
|
event.chatMessage { conversationId, messageId ->
|
||||||
val foreground = event.view.foreground
|
event.view.removeForegroundDrawable("deletedMessage")
|
||||||
if (foreground is ColorDrawable && foreground.color == DELETED_MESSAGE_COLOR) {
|
|
||||||
event.view.foreground = null
|
|
||||||
}
|
|
||||||
getServerMessageIdentifier(conversationId, messageId.toLong())?.let { serverMessageId ->
|
getServerMessageIdentifier(conversationId, messageId.toLong())?.let { serverMessageId ->
|
||||||
if (!deletedMessageCache.contains(serverMessageId)) return@chatMessage
|
if (!deletedMessageCache.contains(serverMessageId)) return@chatMessage
|
||||||
} ?: return@chatMessage
|
} ?: return@chatMessage
|
||||||
event.view.foreground = ColorDrawable(DELETED_MESSAGE_COLOR) // red with alpha
|
|
||||||
|
event.view.addForegroundDrawable("deletedMessage", ShapeDrawable(object: Shape() {
|
||||||
|
override fun draw(canvas: Canvas, paint: Paint) {
|
||||||
|
canvas.drawRect(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), Paint().apply {
|
||||||
|
color = DELETED_MESSAGE_COLOR
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,57 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.ShapeDrawable
|
import android.graphics.drawable.ShapeDrawable
|
||||||
import android.graphics.drawable.StateListDrawable
|
import android.graphics.drawable.StateListDrawable
|
||||||
|
import android.graphics.drawable.shapes.Shape
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Switch
|
import android.widget.Switch
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import me.rhunk.snapenhance.Constants
|
import me.rhunk.snapenhance.Constants
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
fun View.applyTheme(componentWidth: Int? = null, hasRadius: Boolean = false, isAmoled: Boolean = true) {
|
fun View.applyTheme(componentWidth: Int? = null, hasRadius: Boolean = false, isAmoled: Boolean = true) {
|
||||||
ViewAppearanceHelper.applyTheme(this, componentWidth, hasRadius, isAmoled)
|
ViewAppearanceHelper.applyTheme(this, componentWidth, hasRadius, isAmoled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val foregroundDrawableListTag = Random.nextInt(0x7000000, 0x7FFFFFFF)
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun View.getForegroundDrawables(): MutableMap<String, Drawable> {
|
||||||
|
return getTag(foregroundDrawableListTag) as? MutableMap<String, Drawable>
|
||||||
|
?: mutableMapOf<String, Drawable>().also {
|
||||||
|
setTag(foregroundDrawableListTag, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun View.updateForegroundDrawable() {
|
||||||
|
foreground = ShapeDrawable(object: Shape() {
|
||||||
|
override fun draw(canvas: Canvas, paint: Paint) {
|
||||||
|
getForegroundDrawables().forEach { (_, drawable) ->
|
||||||
|
drawable.draw(canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.removeForegroundDrawable(tag: String) {
|
||||||
|
getForegroundDrawables().remove(tag)?.let {
|
||||||
|
updateForegroundDrawable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.addForegroundDrawable(tag: String, drawable: Drawable) {
|
||||||
|
getForegroundDrawables()[tag] = drawable
|
||||||
|
updateForegroundDrawable()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
object ViewAppearanceHelper {
|
object ViewAppearanceHelper {
|
||||||
@SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi",
|
@SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi",
|
||||||
"ClickableViewAccessibility"
|
"ClickableViewAccessibility"
|
||||||
|
Reference in New Issue
Block a user