mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-12 13:17:42 +02:00
fix(e2ee): bind view foreground
- fix MessagingRuleType npe - fix ProtoReader exception
This commit is contained in:
@ -520,13 +520,19 @@
|
||||
"name": "No Friend Score Delay",
|
||||
"description": "Removes the delay when viewing a Friends Score"
|
||||
},
|
||||
"e2e_encryption": {
|
||||
"e2ee": {
|
||||
"name": "End-To-End Encryption",
|
||||
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!"
|
||||
},
|
||||
"encrypted_message_indicator": {
|
||||
"name": "Encrypted Message Indicator",
|
||||
"description": "Adds a \uD83D\uDD12 emoji next to encrypted messages"
|
||||
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!",
|
||||
"properties": {
|
||||
"encrypted_message_indicator": {
|
||||
"name": "Encrypted Message Indicator",
|
||||
"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": {
|
||||
"name": "Add Friend Source Spoof",
|
||||
|
@ -134,7 +134,7 @@ class BridgeClient(
|
||||
fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends)
|
||||
|
||||
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> {
|
||||
|
@ -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 unlimitedMultiSnap = boolean("unlimited_multi_snap") { addNotices(FeatureNotice.BAN_RISK)}
|
||||
val noFriendScoreDelay = boolean("no_friend_score_delay")
|
||||
val useE2EEncryption = boolean("e2e_encryption")
|
||||
val encryptedMessageIndicator = boolean("encrypted_message_indicator") { addNotices(FeatureNotice.UNSTABLE) }
|
||||
val e2eEncryption = container("e2ee", E2EEConfig())
|
||||
val hiddenSnapchatPlusFeatures = boolean("hidden_snapchat_plus_features") { addNotices(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE) }
|
||||
val addFriendSourceSpoof = unique("add_friend_source_spoof",
|
||||
"added_by_username",
|
||||
|
@ -76,7 +76,9 @@ class EventDispatcher(
|
||||
interactionType = interactionType,
|
||||
conversationId = conversationId,
|
||||
messageId = messageId
|
||||
)
|
||||
).apply {
|
||||
adapter = param
|
||||
}
|
||||
) {
|
||||
postHookEvent()
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ abstract class AbstractHookEvent : Event() {
|
||||
}
|
||||
|
||||
fun invokeOriginal() {
|
||||
canceled = true
|
||||
invokeLater()
|
||||
adapter.invokeOriginal()
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ enum class MessagingRuleType(
|
||||
}
|
||||
|
||||
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() {
|
||||
while (offset < buffer.size) {
|
||||
val tag = readVarInt().toInt()
|
||||
val id = tag ushr 3
|
||||
val type = WireType.fromValue(tag and 0x7) ?: break
|
||||
try {
|
||||
val tag = readVarInt().toInt()
|
||||
val id = tag ushr 3
|
||||
val type = WireType.fromValue(tag and 0x7) ?: break
|
||||
val value = when (type) {
|
||||
WireType.VARINT -> readVarInt()
|
||||
WireType.FIXED64 -> {
|
||||
|
@ -1,13 +1,16 @@
|
||||
package me.rhunk.snapenhance.features.impl.experiments
|
||||
|
||||
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.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
|
||||
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.hookConstructor
|
||||
import me.rhunk.snapenhance.ui.ViewAppearanceHelper
|
||||
import me.rhunk.snapenhance.ui.addForegroundDrawable
|
||||
import me.rhunk.snapenhance.ui.removeForegroundDrawable
|
||||
import java.security.MessageDigest
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -37,7 +42,7 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
MessagingRuleType.E2E_ENCRYPTION,
|
||||
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() }
|
||||
|
||||
companion object {
|
||||
@ -185,13 +190,11 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
}
|
||||
}
|
||||
|
||||
val encryptedMessageIndicator by context.config.experimental.encryptedMessageIndicator
|
||||
val chatMessageContentContainerId = context.resources.getIdentifier("chat_message_content_container", "id", context.androidContext.packageName)
|
||||
val encryptedMessageIndicator by context.config.experimental.e2eEncryption.encryptedMessageIndicator
|
||||
|
||||
// hook view binder to add special buttons
|
||||
val receivePublicKeyTag = Random.nextLong().toString(16)
|
||||
val receiveSecretTag = Random.nextLong().toString(16)
|
||||
val encryptedMessageTag = Random.nextLong().toString(16)
|
||||
|
||||
context.event.subscribe(BindViewEvent::class) { event ->
|
||||
event.chatMessage { conversationId, messageId ->
|
||||
@ -206,28 +209,15 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
}
|
||||
|
||||
if (encryptedMessageIndicator) {
|
||||
viewGroup.findViewWithTag<ViewGroup>(encryptedMessageTag)?.also {
|
||||
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage
|
||||
it.removeView(chatMessageContentContainer)
|
||||
viewGroup.removeView(it)
|
||||
viewGroup.addView(chatMessageContentContainer, 0)
|
||||
}
|
||||
viewGroup.removeForegroundDrawable("encryptedMessage")
|
||||
|
||||
if (encryptedMessages.contains(messageId.toLong())) {
|
||||
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage
|
||||
viewGroup.removeView(chatMessageContentContainer)
|
||||
|
||||
viewGroup.addView(RelativeLayout(viewGroup.context).apply {
|
||||
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)
|
||||
viewGroup.addForegroundDrawable("encryptedMessage", ShapeDrawable(object: Shape() {
|
||||
override fun draw(canvas: Canvas, paint: Paint) {
|
||||
paint.textSize = 20f
|
||||
canvas.drawText("\uD83D\uDD12", 0f, canvas.height / 2f, paint)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,13 +345,25 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
|
||||
override fun asyncInit() {
|
||||
if (!isEnabled) return
|
||||
// trick to disable fidelius encryption
|
||||
context.event.subscribe(SendMessageWithContentEvent::class) { param ->
|
||||
val messageContent = param.messageContent
|
||||
val destinations = param.destinations
|
||||
if (destinations.conversations.none { getState(it.toString()) }) return@subscribe
|
||||
val forceMessageEncryption by context.config.experimental.e2eEncryption.forceMessageEncryption
|
||||
|
||||
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) {
|
||||
messageContent.contentType = ContentType.EXTERNAL_MEDIA
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
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 com.google.gson.JsonObject
|
||||
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.hook.HookStage
|
||||
import me.rhunk.snapenhance.hook.Hooker
|
||||
import me.rhunk.snapenhance.ui.addForegroundDrawable
|
||||
import me.rhunk.snapenhance.ui.removeForegroundDrawable
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.time.measureTime
|
||||
@ -155,14 +160,18 @@ class MessageLogger : Feature("MessageLogger",
|
||||
|
||||
context.event.subscribe(BindViewEvent::class) { event ->
|
||||
event.chatMessage { conversationId, messageId ->
|
||||
val foreground = event.view.foreground
|
||||
if (foreground is ColorDrawable && foreground.color == DELETED_MESSAGE_COLOR) {
|
||||
event.view.foreground = null
|
||||
}
|
||||
event.view.removeForegroundDrawable("deletedMessage")
|
||||
getServerMessageIdentifier(conversationId, messageId.toLong())?.let { serverMessageId ->
|
||||
if (!deletedMessageCache.contains(serverMessageId)) 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.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.StateListDrawable
|
||||
import android.graphics.drawable.shapes.Shape
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Switch
|
||||
import android.widget.TextView
|
||||
import me.rhunk.snapenhance.Constants
|
||||
import kotlin.random.Random
|
||||
|
||||
fun View.applyTheme(componentWidth: Int? = null, hasRadius: Boolean = false, isAmoled: Boolean = true) {
|
||||
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 {
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi",
|
||||
"ClickableViewAccessibility"
|
||||
|
Reference in New Issue
Block a user