Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified app/libs/snapmod.jar
Binary file not shown.
2 changes: 2 additions & 0 deletions app/src/main/java/xyz/rodit/snapmod/CustomResources.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ object CustomResources {
string.menu_option_stealth_mode to "Stealth Mode",
string.menu_option_preview to "More Information",
string.menu_option_auto_save to "Auto-Save Messages",
string.menu_option_auto_download to "Auto-Download Snaps",

string.chat_action_playback_speed to "Set Playback Speed"
)
Expand Down Expand Up @@ -39,6 +40,7 @@ object CustomResources {
const val menu_option_stealth_mode = -100000
const val menu_option_preview = -100001
const val menu_option_auto_save = -100002
const val menu_option_auto_download = -100003

const val chat_action_playback_speed = -200000
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import xyz.rodit.xposed.client.http.StreamServer
private const val PINNED_CONVERSATIONS_FILE = "pinned.list"
private const val STEALTH_CONVERSATIONS_FILE = "stealth.list"
private const val AUTO_SAVE_CONVERSATIONS_FILE = "auto_save.list"
private const val AUTO_DOWNLOAD_CONVERSATIONS_FILE = "auto_download.list"

class FeatureContext(
val appContext: Context,
Expand All @@ -25,6 +26,7 @@ class FeatureContext(
val pinned: ConversationManager = ConversationManager(appContext.filesDir, PINNED_CONVERSATIONS_FILE)
val stealth: ConversationManager = ConversationManager(appContext.filesDir, STEALTH_CONVERSATIONS_FILE)
val autoSave: ConversationManager = ConversationManager(appContext.filesDir, AUTO_SAVE_CONVERSATIONS_FILE)
val autoDownload: ConversationManager = ConversationManager(appContext.filesDir, AUTO_DOWNLOAD_CONVERSATIONS_FILE)

var activity: Activity? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import xyz.rodit.snapmod.features.messagemenu.MessageMenuModifier
import xyz.rodit.snapmod.features.notifications.FilterTypes
import xyz.rodit.snapmod.features.notifications.ShowMessageContent
import xyz.rodit.snapmod.features.opera.OperaModelModifier
import xyz.rodit.snapmod.features.saving.AutoDownloadSnaps
import xyz.rodit.snapmod.features.saving.ChatSaving
import xyz.rodit.snapmod.features.saving.PublicProfileSaving
import xyz.rodit.snapmod.features.saving.StoriesSaving
Expand Down Expand Up @@ -45,6 +46,7 @@ class FeatureManager(context: FeatureContext) : Contextual(context) {
add(::OperaModelModifier)

// Saving
add(::AutoDownloadSnaps)
add(::ChatSaving)
add(::PublicProfileSaving)
add(::StoriesSaving)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package xyz.rodit.snapmod.features.chatmenu

import xyz.rodit.snapmod.CustomResources.string.menu_option_auto_download
import xyz.rodit.snapmod.CustomResources.string.menu_option_auto_save
import xyz.rodit.snapmod.CustomResources.string.menu_option_stealth_mode
import xyz.rodit.snapmod.Shared
import xyz.rodit.snapmod.features.Feature
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.mappings.FriendChatActionHandler
Expand All @@ -11,16 +15,28 @@ import xyz.rodit.snapmod.util.before

const val EVENT_PREFIX = "CUSTOM_ACTION"
const val EVENT_DELIMITER = "\u0000:\u0000"
const val PIN_STRING_NAME = "action_menu_pin_conversation"

class ChatMenuModifier(context: FeatureContext) : Feature(context) {

private val plugins: MutableMap<String, MenuPlugin> = HashMap()

override fun init() {
registerPlugin(PinOption(context))
registerPlugin(StealthOption(context))
registerPlugin(PreviewOption(context))
registerPlugin(AutoSaveOption(context))

val pinTextResource = context.appContext.resources.getIdentifier(
PIN_STRING_NAME,
"string",
Shared.SNAPCHAT_PACKAGE
)
registerConversationToggle("pinning", pinTextResource) { it.pinned }
registerConversationToggle("stealth", menu_option_stealth_mode) { it.stealth }
registerConversationToggle("auto_save", menu_option_auto_save) { it.autoSave }
registerConversationToggle("auto_download", menu_option_auto_download) { it.autoDownload }
}

private fun registerConversationToggle(name: String, textResource: Int, manager: Manager) {
registerPlugin(ConversationToggleOption(context, name, textResource, manager))
}

private fun registerPlugin(plugin: MenuPlugin) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package xyz.rodit.snapmod.features.chatmenu

import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.util.ConversationManager
import xyz.rodit.snapmod.util.getList

typealias Manager = (FeatureContext) -> ConversationManager

class ConversationToggleOption(
context: FeatureContext,
name: String,
textResource: Int,
private val manager: Manager
) : ToggleOption(context, name, textResource) {

override fun shouldCreate() = !context.config.getList("hidden_chat_options").contains(name)

override fun isToggled(key: String?) = manager(context).isEnabled(key)

override fun handleEvent(data: String?) = manager(context).toggle(data)
}
25 changes: 0 additions & 25 deletions app/src/main/java/xyz/rodit/snapmod/features/chatmenu/PinOption.kt

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package xyz.rodit.snapmod.features.saving

import xyz.rodit.snapmod.features.Feature
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.mappings.*
import xyz.rodit.snapmod.util.PathManager
import xyz.rodit.snapmod.util.after
import xyz.rodit.snapmod.util.toUUIDString

class AutoDownloadSnaps(context: FeatureContext) : Feature(context) {

private val ignore = hashSetOf<String>()

override fun performHooks() {
OperaPageViewController.onDisplayStateChanged.after {
val viewController = OperaPageViewController.wrap(it.thisObject)
if (viewController.state.instance != OperaDisplayState.FULLY_DISPLAYED().instance) return@after

val params = ParamsMap.wrap(viewController.metadata.instance)
val map = params.map
if (!map.containsKey(MessageStoryKeys.getSnapInSavedState().instance)) return@after

val messageId = map[MessageStoryKeys.getMessageId().instance] as String?
val conversationId = UUID.wrap(map[ConversationStoryKeys.getConversationId().instance])
if (messageId == null || conversationId.isNull) return@after

if (!context.config.getBoolean("auto_download_snaps")
&& !context.autoDownload.isEnabled(conversationId.toUUIDString())
) return@after

if (ignore.contains(messageId)) return@after

ignore.add(messageId)
getMediaInfo(context, params) { info ->
downloadOperaMedia(
context,
PathManager.DOWNLOAD_SNAP,
info
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ package xyz.rodit.snapmod.features.saving

import xyz.rodit.snapmod.features.Feature
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.logging.log
import xyz.rodit.snapmod.mappings.*
import xyz.rodit.snapmod.util.PathManager
import xyz.rodit.snapmod.util.after
import xyz.rodit.snapmod.util.download
import xyz.rodit.xposed.client.http.StreamProvider
import xyz.rodit.xposed.client.http.streams.FileProxyStreamProvider
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.net.URL

class StoriesSaving(context: FeatureContext) : Feature(context) {

Expand Down Expand Up @@ -42,36 +37,11 @@ class StoriesSaving(context: FeatureContext) : Feature(context) {
if (method.name != ContextClickHandler.invoke.dexName) return null

val map = ParamsMap.wrap(args!![0])
getMediaInfo(context, map, this::downloadStoryMedia)

return null
}

private fun downloadStoryMedia(media: StoryMedia?) {
if (media == null || media.info.isNull) return

val provider: StreamProvider = FileProxyStreamProvider(context.appContext) {
try {
var stream = URL(media.info.uri).openStream()
val enc = media.info.encryption
if (enc.isNotNull) {
stream = enc.decryptStream(stream)
}

return@FileProxyStreamProvider stream
} catch (e: Exception) {
log.error("Error opening story media stream.", e)
}
return@FileProxyStreamProvider null
getMediaInfo(context, map) {
downloadOperaMedia(context, PathManager.DOWNLOAD_STORY, it)
}

context.download(
PathManager.DOWNLOAD_STORY,
mapOf("u" to media.username),
media.extension,
provider,
media.username + "'s Story"
)
return null
}
}
}
37 changes: 35 additions & 2 deletions app/src/main/java/xyz/rodit/snapmod/features/saving/StoryHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import xyz.rodit.snapmod.createDummyProxy
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.logging.XLog
import xyz.rodit.snapmod.mappings.*
import xyz.rodit.snapmod.util.download
import xyz.rodit.snapmod.util.toSnapUUID
import xyz.rodit.snapmod.util.toUUIDString
import xyz.rodit.xposed.client.http.StreamProvider
import xyz.rodit.xposed.client.http.streams.FileProxyStreamProvider
import java.net.URL

private val mLog = XLog("StoryHelper")

Expand All @@ -14,8 +18,10 @@ typealias UsernameFetcher = (Map<*, *>) -> String?
private val usernameFetchers = listOf<UsernameFetcher>(
{
val session = ContextSession.wrap(it[ContextStoryKeys.getContextSession().instance])
val snapUsername = session.info.username
if (snapUsername.isNotNull) snapUsername.displayString else null
if (session.isNull) null else {
val snapUsername = session.info.username
if (snapUsername.isNotNull) snapUsername.displayString else null
}
},
{
val storySnap =
Expand All @@ -24,6 +30,33 @@ private val usernameFetchers = listOf<UsernameFetcher>(
}
)

fun downloadOperaMedia(context: FeatureContext, type: String, media: StoryMedia?) {
if (media == null || media.info.isNull) return

val provider: StreamProvider = FileProxyStreamProvider(context.appContext) {
try {
var stream = URL(media.info.uri).openStream()
val enc = media.info.encryption
if (enc.isNotNull) {
stream = enc.decryptStream(stream)
}

return@FileProxyStreamProvider stream
} catch (e: Exception) {
mLog.error("Error opening story media stream.", e)
}
return@FileProxyStreamProvider null
}

context.download(
type,
mapOf("u" to media.username),
media.extension,
provider,
media.username + "'s Story"
)
}

fun getMediaInfo(
context: FeatureContext, metadata: ParamsMap, callback: (StoryMedia?) -> Unit
) {
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/res/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@
<item>NOTE</item>
</string-array>

<string-array name="chat_options">
<item>Pin</item>
<item>Stealth</item>
<item>Auto Save</item>
<item>Auto Download Snaps</item>
</string-array>

<string-array name="chat_options_values">
<item>pinning</item>
<item>stealth</item>
<item>auto_save</item>
<item>auto_download</item>
</string-array>

<string-array name="auto_save_types_defaults">
<item>CHAT</item>
<item>STICKER</item>
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,10 @@

<string name="filtered_notification_types_title">Filtered Message Types</string>
<string name="filtered_notification_types_description">Notifications will not be shown for filtered message types.</string>

<string name="hidden_chat_options_title">Hidden Chat Options</string>
<string name="hidden_chat_options_description">Hides extra chat options (if you feel the chat menu is too cluttered, for example).</string>

<string name="auto_download_snaps_title">Auto-Download Snaps</string>
<string name="auto_download_snaps_description">Automatically downloads snaps when viewed.</string>
</resources>
Loading