diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 526b4c2..a2d7c21 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -13,7 +13,6 @@
-
diff --git a/app/libs/snapmod.jar b/app/libs/snapmod.jar
index cc18dd8..0ae7dfd 100644
Binary files a/app/libs/snapmod.jar and b/app/libs/snapmod.jar differ
diff --git a/app/src/main/java/xyz/rodit/snapmod/CustomResources.kt b/app/src/main/java/xyz/rodit/snapmod/CustomResources.kt
index 03a2efd..ad2ef44 100644
--- a/app/src/main/java/xyz/rodit/snapmod/CustomResources.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/CustomResources.kt
@@ -14,7 +14,10 @@ object CustomResources {
string.menu_option_auto_download to "Auto-Download Snaps",
string.menu_option_export to "Export...",
- string.chat_action_playback_speed to "Set Playback Speed"
+ string.chat_action_playback_speed to "Set Playback Speed",
+
+ string.menu_story_enable_auto_download to "Enable Auto-Download Stories",
+ string.menu_story_disable_auto_download to "Disable Auto-Download Stories"
)
fun init() {
@@ -37,6 +40,10 @@ object CustomResources {
})
}
+ fun get(id: Int): String? {
+ return strings[id]
+ }
+
object string {
const val menu_option_stealth_mode = -100000
const val menu_option_preview = -100001
@@ -45,5 +52,8 @@ object CustomResources {
const val menu_option_export = -100004
const val chat_action_playback_speed = -200000
+
+ const val menu_story_enable_auto_download = -300000
+ const val menu_story_disable_auto_download = -300001
}
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/SnapHooks.kt b/app/src/main/java/xyz/rodit/snapmod/SnapHooks.kt
index b446e18..8d21a64 100644
--- a/app/src/main/java/xyz/rodit/snapmod/SnapHooks.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/SnapHooks.kt
@@ -8,10 +8,12 @@ import android.content.Intent
import android.os.Handler
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
+import de.robv.android.xposed.XposedHelpers
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.features.FeatureManager
import xyz.rodit.snapmod.features.InstanceManager
import xyz.rodit.snapmod.logging.XLog
+import xyz.rodit.snapmod.logging.dump
import xyz.rodit.snapmod.logging.log
import xyz.rodit.snapmod.mappings.MainActivity
import xyz.rodit.snapmod.util.getList
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/FeatureContext.kt b/app/src/main/java/xyz/rodit/snapmod/features/FeatureContext.kt
index be4d182..d531f4d 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/FeatureContext.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/FeatureContext.kt
@@ -13,6 +13,7 @@ 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"
+private const val AUTO_DOWNLOAD_STORIES_FILE = "auto_download_stories.list"
class FeatureContext(
val appContext: Context,
@@ -28,6 +29,7 @@ class FeatureContext(
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)
+ val autoDownloadStories: ConversationManager = ConversationManager(appContext.filesDir, AUTO_DOWNLOAD_STORIES_FILE)
val arroyo = ArroyoReader(appContext)
var activity: Activity? = null
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/FeatureManager.kt b/app/src/main/java/xyz/rodit/snapmod/features/FeatureManager.kt
index f36accc..4aecfa3 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/FeatureManager.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/FeatureManager.kt
@@ -10,10 +10,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
+import xyz.rodit.snapmod.features.saving.*
import xyz.rodit.snapmod.features.tweaks.*
class FeatureManager(context: FeatureContext) : Contextual(context) {
@@ -49,6 +46,7 @@ class FeatureManager(context: FeatureContext) : Contextual(context) {
// Saving
add(::AutoDownloadSnaps)
+ add(::AutoDownloadStories)
add(::ChatSaving)
add(::PublicProfileSaving)
add(::StoriesSaving)
@@ -67,6 +65,7 @@ class FeatureManager(context: FeatureContext) : Contextual(context) {
add(::HideFriends)
add(::HideStoryReadReceipts)
add(::HideStorySections)
+ add(::HideStorySectionsLegacy)
add(::LocationOverride)
}
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ButtonOption.kt b/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ButtonOption.kt
index d4bd9bf..9188122 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ButtonOption.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ButtonOption.kt
@@ -7,7 +7,7 @@ abstract class ButtonOption(context: FeatureContext, name: String, private val t
MenuPlugin(context, name) {
override fun createModel(key: String?): Any {
- val actionDataModel = SendChatActionDataModel(createEventData(key!!), false, null)
+ val actionDataModel = SendChatActionDataModel(createEventData(key!!), false)
val action = SendChatAction(actionDataModel)
val textViewModel = ActionMenuOptionTextViewModel(textResource, null, null, null, null, 62)
return ActionMenuOptionItemViewModel(
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ToggleOption.kt b/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ToggleOption.kt
index f680469..67b5407 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ToggleOption.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/chatmenu/ToggleOption.kt
@@ -9,7 +9,7 @@ abstract class ToggleOption(context: FeatureContext, name: String, private val t
protected abstract fun isToggled(key: String?): Boolean
override fun createModel(key: String?): Any {
- val actionDataModel = SendChatActionDataModel(createEventData(key!!), false, null)
+ val actionDataModel = SendChatActionDataModel(createEventData(key!!), false)
val action = SendChatAction(actionDataModel)
val textViewModel = ActionMenuOptionTextViewModel(textResource, null, null, null, null, 62)
return ActionMenuOptionToggleItemViewModel(
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/opera/AutoSaveOption.kt b/app/src/main/java/xyz/rodit/snapmod/features/opera/AutoSaveOption.kt
new file mode 100644
index 0000000..cd6b531
--- /dev/null
+++ b/app/src/main/java/xyz/rodit/snapmod/features/opera/AutoSaveOption.kt
@@ -0,0 +1,28 @@
+package xyz.rodit.snapmod.features.opera
+
+import xyz.rodit.snapmod.CustomResources
+import xyz.rodit.snapmod.features.Contextual
+import xyz.rodit.snapmod.features.FeatureContext
+import xyz.rodit.snapmod.features.saving.storyId
+import xyz.rodit.snapmod.mappings.OperaActionMenuOptionViewModel
+import xyz.rodit.snapmod.mappings.OperaContextAction
+import xyz.rodit.snapmod.mappings.ParamsMap
+
+class AutoSaveOption(context: FeatureContext) : Contextual(context), MenuPlugin {
+
+ override val isEnabled = true
+
+ override fun createActions(params: ParamsMap): Collection {
+ return setOf(
+ OperaActionMenuOptionViewModel(
+ 0,
+ if (context.autoDownloadStories.isEnabled(params.storyId))
+ CustomResources.string.menu_story_disable_auto_download
+ else
+ CustomResources.string.menu_story_enable_auto_download,
+ true,
+ OperaContextAction.HIDE_AD()
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuModifier.kt b/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuModifier.kt
index a449995..ed7f9d8 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuModifier.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuModifier.kt
@@ -3,6 +3,7 @@ package xyz.rodit.snapmod.features.opera
import xyz.rodit.snapmod.features.Contextual
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.mappings.OperaActionMenuOptionViewModel
+import xyz.rodit.snapmod.mappings.ParamsMap
private const val KEY_NAME = "action_menu_options"
@@ -15,17 +16,17 @@ class MenuModifier(context: FeatureContext) : Contextual(context), OperaPlugin {
return true
}
- override fun shouldOverride(key: String): Boolean {
+ override fun shouldOverride(params: ParamsMap, key: String): Boolean {
return KEY_NAME == key
}
- override fun override(key: String, value: Any): Any {
+ override fun override(params: ParamsMap, key: String, value: Any): Any {
if (value !is List<*>) return value
val newList: MutableList = ArrayList()
newList.addAll(value.filterNotNull())
newList.addAll(
plugins.filter(MenuPlugin::isEnabled)
- .flatMap(MenuPlugin::createActions)
+ .flatMap { it.createActions(params) }
.map(OperaActionMenuOptionViewModel::instance)
)
return newList
@@ -33,5 +34,6 @@ class MenuModifier(context: FeatureContext) : Contextual(context), OperaPlugin {
init {
plugins.add(SaveMenuOption(context))
+ plugins.add(AutoSaveOption(context))
}
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuPlugin.kt b/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuPlugin.kt
index 1ff1baa..23a6551 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuPlugin.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/opera/MenuPlugin.kt
@@ -1,9 +1,10 @@
package xyz.rodit.snapmod.features.opera
import xyz.rodit.snapmod.mappings.OperaActionMenuOptionViewModel
+import xyz.rodit.snapmod.mappings.ParamsMap
interface MenuPlugin {
val isEnabled: Boolean
- fun createActions(): Collection
+ fun createActions(params: ParamsMap): Collection
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaModelModifier.kt b/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaModelModifier.kt
index 7930416..1a23655 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaModelModifier.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaModelModifier.kt
@@ -18,9 +18,10 @@ class OperaModelModifier(context: FeatureContext) : Feature(context) {
override fun performHooks() {
// Modify opera model map on insert.
ParamsMap.put.before {
+ val params = ParamsMap.wrap(it.thisObject)
val key = MapKey.wrap(it.args[0]).name
- plugins.filter { p -> p.isEnabled && p.shouldOverride(key) }
- .forEach { p -> it.args[1] = p.override(key, it.args[1]) }
+ plugins.filter { p -> p.isEnabled && p.shouldOverride(params, key) }
+ .forEach { p -> it.args[1] = p.override(params, key, it.args[1]) }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaPlugin.kt b/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaPlugin.kt
index a2159c4..3ba4fa3 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaPlugin.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/opera/OperaPlugin.kt
@@ -1,8 +1,10 @@
package xyz.rodit.snapmod.features.opera
+import xyz.rodit.snapmod.mappings.ParamsMap
+
interface OperaPlugin {
val isEnabled: Boolean
- fun shouldOverride(key: String): Boolean
- fun override(key: String, value: Any): Any
+ fun shouldOverride(params: ParamsMap, key: String): Boolean
+ fun override(params: ParamsMap, key: String, value: Any): Any
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/opera/SaveMenuOption.kt b/app/src/main/java/xyz/rodit/snapmod/features/opera/SaveMenuOption.kt
index 7aa49fa..4dacbd4 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/opera/SaveMenuOption.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/opera/SaveMenuOption.kt
@@ -4,13 +4,14 @@ import xyz.rodit.snapmod.features.Contextual
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.mappings.OperaActionMenuOptionViewModel
import xyz.rodit.snapmod.mappings.OperaContextActions
+import xyz.rodit.snapmod.mappings.ParamsMap
class SaveMenuOption(context: FeatureContext) : Contextual(context), MenuPlugin {
override val isEnabled: Boolean
get() = context.config.getBoolean("allow_download_stories")
- override fun createActions(): Collection {
+ override fun createActions(params: ParamsMap): Collection {
return setOf(OperaContextActions.getSaveAction())
}
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/opera/SnapDurationModifier.kt b/app/src/main/java/xyz/rodit/snapmod/features/opera/SnapDurationModifier.kt
index 6e4ebcd..b014dd2 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/opera/SnapDurationModifier.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/opera/SnapDurationModifier.kt
@@ -2,6 +2,7 @@ package xyz.rodit.snapmod.features.opera
import xyz.rodit.snapmod.features.Contextual
import xyz.rodit.snapmod.features.FeatureContext
+import xyz.rodit.snapmod.mappings.ParamsMap
import xyz.rodit.snapmod.mappings.StoryAutoAdvanceMode
import xyz.rodit.snapmod.mappings.StoryMediaPlaybackMode
@@ -13,11 +14,11 @@ class SnapDurationModifier(context: FeatureContext) : Contextual(context), Opera
override val isEnabled: Boolean
get() = context.config.getBoolean("unlimited_snap_duration")
- override fun shouldOverride(key: String): Boolean {
+ override fun shouldOverride(params: ParamsMap, key: String): Boolean {
return KEY_AUTO_ADVANCE_MODE == key || KEY_MEDIA_PLAYBACK_MODE == key
}
- override fun override(key: String, value: Any): Any {
+ override fun override(params: ParamsMap, key: String, value: Any): Any {
if (KEY_AUTO_ADVANCE_MODE == key) {
return StoryAutoAdvanceMode.NO_AUTO_ADVANCE().instance
} else if (KEY_MEDIA_PLAYBACK_MODE == key) {
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/saving/AutoDownloadSnaps.kt b/app/src/main/java/xyz/rodit/snapmod/features/saving/AutoDownloadSnaps.kt
index 7ef63ac..c3005e3 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/saving/AutoDownloadSnaps.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/saving/AutoDownloadSnaps.kt
@@ -19,7 +19,7 @@ class AutoDownloadSnaps(context: FeatureContext) : Feature(context, 84608.toMax(
val params = ParamsMap.wrap(viewController.metadata.instance)
val map = params.map
- if (!map.containsKey(MessageStoryKeys.getSnapInSavedState().instance)) return@after
+ if (!params.isChat) return@after
val messageId = map[MessageStoryKeys.getMessageId().instance] as String?
val conversationId = UUID.wrap(map[ConversationStoryKeys.getConversationId().instance])
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/saving/AutoDownloadStories.kt b/app/src/main/java/xyz/rodit/snapmod/features/saving/AutoDownloadStories.kt
new file mode 100644
index 0000000..4ce3d69
--- /dev/null
+++ b/app/src/main/java/xyz/rodit/snapmod/features/saving/AutoDownloadStories.kt
@@ -0,0 +1,36 @@
+package xyz.rodit.snapmod.features.saving
+
+import xyz.rodit.snapmod.features.Feature
+import xyz.rodit.snapmod.features.FeatureContext
+import xyz.rodit.snapmod.mappings.OperaDisplayState
+import xyz.rodit.snapmod.mappings.OperaPageViewController
+import xyz.rodit.snapmod.mappings.ParamsMap
+import xyz.rodit.snapmod.util.PathManager
+import xyz.rodit.snapmod.util.after
+import xyz.rodit.snapmod.util.toMax
+
+class AutoDownloadStories(context: FeatureContext) : Feature(context, 84608.toMax()) {
+
+ 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)
+ if (params.isChat) return@after
+
+ val storyId = params.storyId
+ if (!context.config.getBoolean("auto_download_stories")
+ && !context.autoDownloadStories.isEnabled(storyId)
+ ) return@after
+
+ getMediaInfo(context, params) { info ->
+ downloadOperaMedia(
+ context,
+ PathManager.DOWNLOAD_STORY,
+ info
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/saving/ChatSaving.kt b/app/src/main/java/xyz/rodit/snapmod/features/saving/ChatSaving.kt
index dbe7730..3151690 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/saving/ChatSaving.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/saving/ChatSaving.kt
@@ -53,7 +53,7 @@ class ChatSaving(context: FeatureContext) : Feature(context) {
}
// Map live snap model hashCode to media object for download later.
- ChatModelLiveSnap.constructors.after(context, "allow_save_snaps") {
+ ChatModelWithMedia.constructors.after(context, "allow_save_snaps") {
val hashCode = it.thisObject.hashCode()
chatMediaMap[hashCode] = it.args[6]
}
@@ -66,7 +66,7 @@ class ChatSaving(context: FeatureContext) : Feature(context) {
lastMessageData = base.messageData
when {
- ChatModelLiveSnap.isInstance(it.args[2]) -> {
+ ChatModelWithMedia.isInstance(it.args[2]) -> {
// Convert live snap to saved snap.
val hashCode = it.args[2].hashCode()
val media = LiveSnapMedia.wrap(chatMediaMap[hashCode])
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/saving/StoriesSaving.kt b/app/src/main/java/xyz/rodit/snapmod/features/saving/StoriesSaving.kt
index 5139c8f..e32c5ed 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/saving/StoriesSaving.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/saving/StoriesSaving.kt
@@ -1,5 +1,6 @@
package xyz.rodit.snapmod.features.saving
+import xyz.rodit.snapmod.CustomResources
import xyz.rodit.snapmod.createDelegate
import xyz.rodit.snapmod.features.Feature
import xyz.rodit.snapmod.features.FeatureContext
@@ -7,25 +8,41 @@ import xyz.rodit.snapmod.mappings.ContextActionMenuModel
import xyz.rodit.snapmod.mappings.Func1
import xyz.rodit.snapmod.mappings.OperaContextAction
import xyz.rodit.snapmod.mappings.ParamsMap
-import xyz.rodit.snapmod.util.PathManager
import xyz.rodit.snapmod.util.after
class StoriesSaving(context: FeatureContext) : Feature(context) {
override fun performHooks() {
// Override save story click.
+ // Override custom options click.
ContextActionMenuModel.constructors.after(context, "allow_download_stories") {
val model = ContextActionMenuModel.wrap(it.thisObject)
- if (model.action.instance !== OperaContextAction.SAVE().instance) return@after
-
- val clickProxy = Func1.getMappedClass().createDelegate(context.classLoader) { _, args ->
- val map = ParamsMap.wrap(args[0])
- getMediaInfo(context, map) {
- downloadOperaMedia(context, PathManager.DOWNLOAD_STORY, it)
- }
- null
+ if (model.action.instance == OperaContextAction.SAVE().instance) {
+ val clickProxy =
+ Func1.getMappedClass().createDelegate(context.classLoader) { _, args ->
+ val params = ParamsMap.wrap(args[0])
+ getMediaInfo(context, params) { info ->
+ downloadOperaMedia(context, null, info)
+ }
+ null
+ }
+ model.onClick = Func1.wrap(clickProxy)
+ } else if (model.text == CustomResources.get(CustomResources.string.menu_story_disable_auto_download)) {
+ model.onClick = createAutoDownloadClickProxy(false)
+ } else if (model.text == CustomResources.get(CustomResources.string.menu_story_enable_auto_download)) {
+ model.onClick = createAutoDownloadClickProxy(true)
}
- model.onClick = Func1.wrap(clickProxy)
}
}
+
+ private fun createAutoDownloadClickProxy(enable: Boolean) =
+ Func1.wrap(
+ Func1.getMappedClass().createDelegate(context.classLoader) { _, args ->
+ val params = ParamsMap.wrap(args[0])
+ val storyId = params.storyId
+ if (enable) context.autoDownloadStories.enable(storyId)
+ else context.autoDownloadStories.disable(storyId)
+ null
+ }
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/saving/StoryHelper.kt b/app/src/main/java/xyz/rodit/snapmod/features/saving/StoryHelper.kt
index 01eeb4b..3dd1db9 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/saving/StoryHelper.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/saving/StoryHelper.kt
@@ -4,6 +4,7 @@ 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.PathManager
import xyz.rodit.snapmod.util.download
import xyz.rodit.snapmod.util.toSnapUUID
import xyz.rodit.snapmod.util.toUUIDString
@@ -30,7 +31,7 @@ private val usernameFetchers = listOf(
}
)
-fun downloadOperaMedia(context: FeatureContext, type: String, media: StoryMedia?) {
+fun downloadOperaMedia(context: FeatureContext, type: String?, media: StoryMedia?) {
if (media == null || media.info.isNull) return
val provider: StreamProvider = FileProxyStreamProvider(context.appContext) {
@@ -48,12 +49,16 @@ fun downloadOperaMedia(context: FeatureContext, type: String, media: StoryMedia?
return@FileProxyStreamProvider null
}
+ val finalType = type
+ ?: if (media.map.containsKey(MessageStoryKeys.getMessageId().instance)) PathManager.DOWNLOAD_SNAP else PathManager.DOWNLOAD_STORY
+ val typeString = if (finalType == PathManager.DOWNLOAD_SNAP) "Snap" else "Story"
+
context.download(
- type,
+ finalType,
mapOf("u" to media.username),
media.extension,
provider,
- media.username + "'s Story"
+ "${media.username}'s $typeString"
)
}
@@ -65,17 +70,17 @@ fun getMediaInfo(
getUsername(context, metadata) { username ->
map[StoryMetadata.getImageMediaInfo().instance]?.let {
mLog.debug("Found image media info for story.")
- return@getUsername callback(StoryMedia(OperaMediaInfo.wrap(it), username, ".jpg"))
+ return@getUsername callback(StoryMedia(OperaMediaInfo.wrap(it), username, ".jpg", map))
}
map[StoryMetadata.getVideoMediaInfo().instance]?.let {
mLog.debug("Found video media info for story.")
- return@getUsername callback(StoryMedia(OperaMediaInfo.wrap(it), username, ".mp4"))
+ return@getUsername callback(StoryMedia(OperaMediaInfo.wrap(it), username, ".mp4", map))
}
map[StoryMetadata.getOverlayImageMediaInfo().instance]?.let {
mLog.debug("Found image overlay media info for story.")
- return@getUsername callback(StoryMedia(OperaMediaInfo.wrap(it), username, ".jpg"))
+ return@getUsername callback(StoryMedia(OperaMediaInfo.wrap(it), username, ".jpg", map))
}
mLog.error("Error getting media info for $metadata.")
@@ -130,4 +135,15 @@ private fun getUsername(
)
}
-class StoryMedia(val info: OperaMediaInfo, val username: String, val extension: String)
\ No newline at end of file
+val ParamsMap.isChat: Boolean
+ get() = map.containsKey(MessageStoryKeys.getSnapInSavedState().instance)
+
+val ParamsMap.storyId: String?
+ get() {
+ val reportingInfo = map[FriendStoryKeys.getStorySnapViewReportingInfo().instance]
+ return if (reportingInfo is Collection<*>) StorySnapViewReportingInfo.wrap(reportingInfo.first()).storySnapKey.storyKey.storyId else null
+ }
+
+class StoryMedia(
+ val info: OperaMediaInfo, val username: String, val extension: String, val map: Map<*, *>
+)
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/tweaks/ConfigurationTweaks.kt b/app/src/main/java/xyz/rodit/snapmod/features/tweaks/ConfigurationTweaks.kt
index 25f0185..440a6e3 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/tweaks/ConfigurationTweaks.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/tweaks/ConfigurationTweaks.kt
@@ -52,6 +52,8 @@ class ConfigurationTweaks(context: FeatureContext) : Feature(context) {
true
)
+ override("ENABLE_DF_FRIENDS_SECTION_LIST_VIEW", "enable_story_list", true)
+
override("ENABLE_VOICE_NOTE_REVAMP", "enable_new_voice_notes", true)
override("ENABLE_VOICE_NOTES_MESSAGING_PLUGIN", "enable_new_voice_notes", true)
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/tweaks/HideStorySections.kt b/app/src/main/java/xyz/rodit/snapmod/features/tweaks/HideStorySections.kt
index b7004ac..a655574 100644
--- a/app/src/main/java/xyz/rodit/snapmod/features/tweaks/HideStorySections.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/features/tweaks/HideStorySections.kt
@@ -7,7 +7,7 @@ import xyz.rodit.snapmod.mappings.DiscoverViewBinder
import xyz.rodit.snapmod.util.before
import xyz.rodit.snapmod.util.getList
-class HideStorySections(context: FeatureContext) : Feature(context) {
+class HideStorySections(context: FeatureContext) : Feature(context, 0L..84609L) {
private val hiddenStorySections: MutableSet = HashSet()
diff --git a/app/src/main/java/xyz/rodit/snapmod/features/tweaks/HideStorySectionsLegacy.kt b/app/src/main/java/xyz/rodit/snapmod/features/tweaks/HideStorySectionsLegacy.kt
new file mode 100644
index 0000000..e5f9c02
--- /dev/null
+++ b/app/src/main/java/xyz/rodit/snapmod/features/tweaks/HideStorySectionsLegacy.kt
@@ -0,0 +1,37 @@
+package xyz.rodit.snapmod.features.tweaks
+
+import xyz.rodit.snapmod.features.Feature
+import xyz.rodit.snapmod.features.FeatureContext
+import xyz.rodit.snapmod.mappings.AdapterBase
+import xyz.rodit.snapmod.mappings.DfSectionController
+import xyz.rodit.snapmod.util.before
+import xyz.rodit.snapmod.util.getList
+import xyz.rodit.snapmod.util.toMax
+
+private val legacyMap = mapOf(
+ "friends" to "FRIENDS_SECTION",
+ "subs" to "SUBSCRIBED_SECTION",
+ "for_you" to "FOR_YOU_SECTION"
+)
+
+class HideStorySectionsLegacy(context: FeatureContext) : Feature(context, 84612L.toMax()) {
+
+ private val hiddenStorySections: MutableSet = HashSet()
+
+ override fun onConfigLoaded(first: Boolean) {
+ hiddenStorySections.clear()
+ hiddenStorySections.addAll(
+ context.config.getList("disable_story_sections").mapNotNull { legacyMap[it] })
+ }
+
+ override fun performHooks() {
+ AdapterBase.addSection.before {
+ if (!DfSectionController.isInstance(it.args[0])) return@before
+
+ val sectionType = DfSectionController.wrap(it.args[0]).sectionType.instance.toString()
+ if (hiddenStorySections.contains(sectionType)) {
+ it.result = null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/rodit/snapmod/util/DownloadExtensions.kt b/app/src/main/java/xyz/rodit/snapmod/util/DownloadExtensions.kt
index a2df957..d0307db 100644
--- a/app/src/main/java/xyz/rodit/snapmod/util/DownloadExtensions.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/util/DownloadExtensions.kt
@@ -23,7 +23,9 @@ fun FeatureContext.download(
description: String? = null
) {
if (config.getBoolean("show_toast_on_download")) {
- Toast.makeText(appContext, "Download started.", Toast.LENGTH_SHORT).show()
+ activity?.runOnUiThread {
+ Toast.makeText(appContext, "Download started.", Toast.LENGTH_SHORT).show()
+ }
}
val uuid = UUID.randomUUID().toString()
diff --git a/app/src/main/java/xyz/rodit/snapmod/util/PathManager.kt b/app/src/main/java/xyz/rodit/snapmod/util/PathManager.kt
index 5d0b34d..53a4def 100644
--- a/app/src/main/java/xyz/rodit/snapmod/util/PathManager.kt
+++ b/app/src/main/java/xyz/rodit/snapmod/util/PathManager.kt
@@ -20,6 +20,7 @@ object PathManager {
private val PATTERN_PUBLIC_DIR = Pattern.compile("""\$(\w+)""")
private val PATTERN_PARAMETER = Pattern.compile("%([A-Za-z]+)")
private val publicDirs: MutableMap = mutableMapOf(
+ "Downloads" to Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path,
"Movies" to Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).path,
"Pictures" to Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).path,
"Alarms" to Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS).path,
@@ -28,10 +29,10 @@ object PathManager {
"Ringtones" to Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES).path
)
private val defaultPaths: MutableMap = mutableMapOf(
- DOWNLOAD_STORY to "\$Movies/SnapMod/%u_story_%d",
- DOWNLOAD_AUDIO_NOTE to "\$Movies/SnapMod/%u_audio_%d",
- DOWNLOAD_PROFILE to "\$Movies/SnapMod/%u_profile_%d",
- DOWNLOAD_SNAP to "\$Movies/SnapMod/%u_snap_%d"
+ DOWNLOAD_STORY to "\$Downloads/SnapMod/%u_story_%d",
+ DOWNLOAD_AUDIO_NOTE to "\$Downloads/SnapMod/%u_audio_%d",
+ DOWNLOAD_PROFILE to "\$Downloads/SnapMod/%u_profile_%d",
+ DOWNLOAD_SNAP to "\$Downloads/SnapMod/%u_snap_%d"
)
private fun appendDefaultParamsMap(config: ConfigurationClient, paramsMap: Map): Map {
@@ -39,7 +40,8 @@ object PathManager {
appended["t"] = System.currentTimeMillis().toString()
val dateFormat =
SimpleDateFormat(
- config.getString("download_date_format", DEFAULT_DATE_FORMAT),
+ config.getString("download_date_format", DEFAULT_DATE_FORMAT)
+ .replace(':', '-'),
Locale.getDefault()
)
appended["d"] = dateFormat.format(Date())
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c3691bb..07b2106 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -50,12 +50,11 @@
Bypass Video Length Restrictions
Story Download Path
+ Snap Download Path
Audio Note Download Path
Public Profile Picture Download Path
- Enable Custom Snap Download Path
- Snaps will be downloaded to the custom path defined below instead of the default \'Internal Storage/Snapchat\' path.
- Set Snap Download Path
- Tap to set the override snap download path.
+ Always Use Snap Download Path
+ Forces the custom snap download path above to be used for saving snaps through the Save to Camera Roll action.
Use Android Download Manager
🧪 Location Sharing Override
@@ -104,6 +103,7 @@
Removes the video time limit restrictions when sending snaps from the gallery drawer in chat.
Stories and snaps will loop and never auto-close or auto-advance.
The path on the device to download stories to.
+ The path on the device to download snaps to.
The path on the device to download audio notes to.
The path on the device to download profile pictures to.
Whether or not to use the Android system download manager. This is required when downloading to public directories on Android 11+. Try turning this off if you have issues downloading media.
@@ -153,6 +153,11 @@
Auto-Download Snaps
Automatically downloads snaps when viewed.
+ Auto-Download Stories
+ Automatically downloads stories when viewed.
+
Enable New Chat Menu
Enables the new chat context menu (recommended).
+ Enable Story List
+ Replaces the default story carousel with a list.
\ No newline at end of file
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index c0a84dc..d2e1e77 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -158,6 +158,12 @@
app:summary="@string/auto_download_snaps_description"
app:iconSpaceReserved="false" />
+
+
+
+
+
+
-
-
(Context, ...)
+}
+
+class ChatModelLiveSnap extends #ChatModelWithMedia {
[discard] boolean b0;
@@ -513,19 +520,19 @@ class FooterInfoItem {
String text;
- void $(String, long) {
+ void $(String, long, ...) {
.field !ProfileSectionType->!FOOTER_INFO_ITEM;
}
}
-class FriendProfileTransformer {
+class FriendProfileTransformer implements #Func2 {
[discard] int i0;
[discard] Object target;
void $(Object, int)
- * $apply(...) {
+ * apply(...) {
.string "Required value was null.";
.string "performanceLogger";
.method Collections->singletonList;
@@ -649,6 +656,8 @@ class MemoriesPickerVideoDurationConfig {
[certain]
class OperaActionMenuOptionViewModel {
+ void $(int, int, boolean, ...)
+
java.lang.String $toString() {
[conserve]
.string contains "OperaActionMenuOptionViewModel";
@@ -656,6 +665,7 @@ class OperaActionMenuOptionViewModel {
.string contains "textResource", bind field textResource next reference;
.string contains "textColorResource", bind field textColorResource next reference;
.string contains "eventName", bind field eventName next reference;
+ .string contains "mShouldDismissActionMenu", bind field shouldDismissActionMenu next reference;
.string contains "isLoading", bind field isLoading next reference;
.string contains "actionMenuId", bind field actionMenuId next reference;
}
@@ -930,10 +940,13 @@ class ContextStoryKeys {
class FriendStoryKeys {
[late] !MapKey playableSnapStoryRecord;
+ [late] !MapKey storySnapViewReportingInfo;
static void $() {
[strict]
.string "PLAYABLE_STORY_SNAP_RECORD", bind field playableSnapStoryRecord next reference;
+ [strict]
+ .string "STORY_SNAP_VIEW_REPORTING_INFO", bind field storySnapViewReportingInfo next reference;
}
}
@@ -1017,6 +1030,7 @@ class ContextSession {
[certain]
class ContextActionMenuModel {
+ String text;
[late] #Func1 onClick;
!OperaContextAction action;
@@ -1765,10 +1779,10 @@ class MessagingNotificationHandler {
[certain]
class NotificationHandler {
- Object data;
+ Object conversationIdProvider;
[discard] int a0;
+ Object data;
Object handler;
- Object conversationIdProvider;
* handle() {
.type !NotificationData;
@@ -1783,6 +1797,10 @@ class AesCrypto {
void $(String, String, ...)
void $(byte[], byte[])
+ Cipher getCipher() {
+ .string "AES/CBC/PKCS5Padding";
+ }
+
InputStream encrypt(InputStream)
InputStream decrypt(InputStream)
@@ -1873,6 +1891,8 @@ class ProfileActionSheetCreator {
enum ActionSheetType {
[late] static this .fields { PROFILE, PROFILE_ACTION_MENU }
+
+ [not] int i0;
}
[certain]
@@ -1947,6 +1967,85 @@ class ProfileActionSheetChooser {
}
}
+[certain]
+class StoryKey {
+
+ String storyId;
+
+ String $toString() {
+ .string "StoryKey(storyId=";
+ .string ", storyKind=";
+ }
+}
+
+[certain]
+class StorySnapKey {
+
+ !StoryKey storyKey;
+
+ String $toString() {
+ .string "StorySnapKey(storyKey=";
+ }
+}
+
+[certain]
+class StorySnapViewReportingInfo {
+
+ String snapOwnerUserId;
+ !StorySnapKey storySnapKey;
+
+ String $toString() {
+ .string "StorySnapViewReportingInfo(viewReportingId=";
+ .string ", snapOwnerUserId=";
+ }
+}
+
+[certain, obfuscated]
+enum DfSectionType {
+
+ [late] static this .fields { FRIENDS_SECTION, SUBSCRIBED_SECTION, FOR_YOU_SECTION, UNKNOWN_SECTION }
+}
+
+[certain]
+class DfSectionController {
+
+ !DfSectionType sectionType;
+
+ [discard] void $(...) {
+ .string "DfSectionController";
+ }
+}
+
+[certain, discard]
+class DiscoverFeedAdapterBase extends #AdapterBase {
+
+ [discard] void $(...) {
+ .string "df:dfa:init";
+ }
+}
+
+[certain]
+class AllTabsDiscoverFeedAdapter extends !DiscoverFeedAdapterBase {
+
+ [discard] void $(...) {
+ .string "AllTabsDiscoverFeedAdapter";
+ }
+}
+
+[late]
+class AdapterBase {
+
+ void addSection(#AdapterSection)
+}
+
+[late]
+interface AdapterSection {
+
+ [discard] String getClassName()
+ [discard] void m0(View, ...)
+ [discard] void m1(View, ...)
+}
+
[late]
interface Func0 {