feat(core): long press to force download

This commit is contained in:
rhunk
2024-01-21 11:00:31 +01:00
parent 05a495d51f
commit 5498f8206d
8 changed files with 87 additions and 48 deletions

View File

@ -94,6 +94,7 @@ class SnapEnhance {
val isMainActivityNotNull = appContext.mainActivity != null
appContext.mainActivity = this
if (isMainActivityNotNull || !appContext.mappings.isMappingsLoaded) return@hookMainActivity
appContext.isMainActivityPaused = false
onActivityCreate()
jetpackComposeResourceHook()
appContext.actionManager.onNewIntent(intent)
@ -108,18 +109,13 @@ class SnapEnhance {
appContext.actionManager.onNewIntent(param.argNullable(0))
}
var activityWasResumed = false
//we need to reload the config when the app is resumed
//FIXME: called twice at first launch
hookMainActivity("onResume") {
appContext.isMainActivityPaused = false
if (!activityWasResumed) {
activityWasResumed = true
return@hookMainActivity
if (appContext.isMainActivityPaused.also {
appContext.isMainActivityPaused = false
}) {
appContext.reloadConfig()
syncRemote()
}
appContext.reloadConfig()
syncRemote()
}
}

View File

@ -75,10 +75,11 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
mediaAuthor: String,
creationTimestamp: Long? = null,
downloadSource: MediaDownloadSource,
friendInfo: FriendInfo? = null
friendInfo: FriendInfo? = null,
forceAllowDuplicate: Boolean = false
): DownloadManagerClient {
val generatedHash = (
if (!context.config.downloader.allowDuplicate.get()) mediaIdentifier
if (!context.config.downloader.allowDuplicate.get() && !forceAllowDuplicate) mediaIdentifier
else UUID.randomUUID().toString()
).longHashCode().absoluteValue.toString(16)
@ -135,10 +136,10 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
/*
* Download the last seen media
*/
fun downloadLastOperaMediaAsync() {
fun downloadLastOperaMediaAsync(allowDuplicate: Boolean) {
if (lastSeenMapParams == null || lastSeenMediaInfoMap == null) return
context.executeAsync {
handleOperaMedia(lastSeenMapParams!!, lastSeenMediaInfoMap!!, true)
handleOperaMedia(lastSeenMapParams!!, lastSeenMediaInfoMap!!, true, allowDuplicate)
}
}
@ -162,9 +163,6 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
setNeutralButton("Copy") { _, _ ->
this@MediaDownloader.context.copyToClipboard(mediaInfoText)
}
setPositiveButton("Download") { _, _ ->
downloadLastOperaMediaAsync()
}
setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
}.show()
}
@ -229,7 +227,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
private fun handleOperaMedia(
paramMap: ParamMap,
mediaInfoMap: Map<SplitMediaAssetType, MediaInfo>,
forceDownload: Boolean
forceDownload: Boolean,
forceAllowDuplicate: Boolean = false
) {
//messages
paramMap["MESSAGE_ID"]?.toString()?.takeIf { forceDownload || canAutoDownload("friend_snaps") }?.let { id ->
@ -257,7 +256,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
mediaAuthor = authorUsername,
creationTimestamp = conversationMessage.creationTimestamp,
downloadSource = MediaDownloadSource.CHAT_MEDIA,
friendInfo = author
friendInfo = author,
forceAllowDuplicate = forceAllowDuplicate
), mediaInfoMap)
return
@ -301,7 +301,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
creationTimestamp = paramMap["PLAYABLE_STORY_SNAP_RECORD"]?.toString()?.substringAfter("timestamp=")
?.substringBefore(",")?.toLongOrNull(),
downloadSource = MediaDownloadSource.STORY,
friendInfo = author
friendInfo = author,
forceAllowDuplicate = forceAllowDuplicate,
), mediaInfoMap)
return
}
@ -331,6 +332,7 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
mediaAuthor = author,
downloadSource = MediaDownloadSource.PUBLIC_STORY,
creationTimestamp = paramMap["SNAP_TIMESTAMP"]?.toString()?.toLongOrNull(),
forceAllowDuplicate = forceAllowDuplicate,
), mediaInfoMap)
return
}
@ -342,6 +344,7 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
downloadSource = MediaDownloadSource.SPOTLIGHT,
mediaAuthor = paramMap["CREATOR_DISPLAY_NAME"].toString(),
creationTimestamp = paramMap["SNAP_TIMESTAMP"]?.toString()?.toLongOrNull(),
forceAllowDuplicate = forceAllowDuplicate,
), mediaInfoMap)
return
}
@ -435,7 +438,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
provideDownloadManagerClient(
mediaIdentifier = "${paramMap["STORY_ID"]}-${firstChapter.offset}-${lastChapter.offset}",
downloadSource = MediaDownloadSource.PUBLIC_STORY,
mediaAuthor = storyName
mediaAuthor = storyName,
forceAllowDuplicate = forceAllowDuplicate,
).downloadDashMedia(
playlistUrl,
firstChapter.offset.plus(100),
@ -505,7 +509,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
friendInfo: FriendInfo,
message: ConversationMessage,
authorName: String,
attachments: List<DecodedAttachment>
attachments: List<DecodedAttachment>,
forceAllowDuplicate: Boolean = false
) {
//TODO: stickers
attachments.forEach { attachment ->
@ -514,7 +519,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
mediaIdentifier = "${message.clientConversationId}${message.senderId}${message.serverMessageId}${attachment.mediaUniqueId}",
downloadSource = MediaDownloadSource.CHAT_MEDIA,
mediaAuthor = authorName,
friendInfo = friendInfo
friendInfo = friendInfo,
forceAllowDuplicate = forceAllowDuplicate,
).downloadSingleMedia(
mediaData = attachment.mediaUrlKey!!,
mediaType = DownloadMediaType.PROTO_MEDIA,
@ -531,7 +537,7 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
@SuppressLint("SetTextI18n")
@OptIn(ExperimentalCoroutinesApi::class)
fun downloadMessageId(messageId: Long, isPreview: Boolean = false) {
fun downloadMessageId(messageId: Long, forceAllowDuplicate: Boolean = false, isPreview: Boolean = false) {
val messageLogger = context.feature(MessageLogger::class)
val message = context.database.getConversationMessageFromId(messageId) ?: throw Exception("Message not found in database")
@ -570,7 +576,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
context.mainActivity == null // we can't show alert dialogs when it downloads from a notification, so it downloads the first one
) {
downloadMessageAttachments(friendInfo, message, authorName,
listOf(decodedAttachments.first())
listOf(decodedAttachments.first()),
forceAllowDuplicate = forceAllowDuplicate
)
return
}
@ -595,7 +602,9 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
setTitle(translations["select_attachments_title"])
setNegativeButton(this@MediaDownloader.context.translation["button.cancel"]) { dialog, _ -> dialog.dismiss() }
setPositiveButton(this@MediaDownloader.context.translation["button.download"]) { _, _ ->
downloadMessageAttachments(friendInfo, message, authorName, selectedAttachments.map { decodedAttachments[it] })
downloadMessageAttachments(friendInfo, message, authorName, selectedAttachments.map { decodedAttachments[it] },
forceAllowDuplicate = forceAllowDuplicate
)
}
}.show()
}
@ -698,9 +707,12 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
/**
* Called when a message is focused in chat
*/
fun onMessageActionMenu(isPreviewMode: Boolean) {
fun onMessageActionMenu(isPreviewMode: Boolean, forceAllowDuplicate: Boolean = false) {
val messaging = context.feature(Messaging::class)
if (messaging.openedConversationUUID == null) return
downloadMessageId(messaging.lastFocusedMessageId, isPreviewMode)
context.executeAsync {
downloadMessageId(messaging.lastFocusedMessageId, forceAllowDuplicate, isPreviewMode)
}
}
}

View File

@ -33,6 +33,7 @@ import me.rhunk.snapenhance.core.ui.triggerCloseTouchEvent
import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.hook
import me.rhunk.snapenhance.core.util.ktx.getDimens
import me.rhunk.snapenhance.core.util.ktx.vibrateLongPress
import java.text.SimpleDateFormat
import java.util.Date
@ -80,7 +81,7 @@ class ChatActionMenu : AbstractMenu() {
override fun init() {
runCatching {
if (!context.config.downloader.chatDownloadContextMenu.get() && context.config.messaging.messageLogger.globalState != true && !context.isDeveloper) return
if (!context.config.downloader.downloadContextMenu.get() && context.config.messaging.messageLogger.globalState != true && !context.isDeveloper) return
context.androidContext.classLoader.loadClass("com.snap.messaging.chat.features.actionmenu.ActionMenuChatItemContainer")
.hook("onMeasure", HookStage.BEFORE) { param ->
param.setArg(1,
@ -130,12 +131,14 @@ class ChatActionMenu : AbstractMenu() {
}
}
if (context.config.downloader.chatDownloadContextMenu.get()) {
if (context.config.downloader.downloadContextMenu.get()) {
val mediaDownloader = context.feature(MediaDownloader::class)
injectButton(Button(viewGroup.context).apply {
text = this@ChatActionMenu.context.translation["chat_action_menu.preview_button"]
setOnClickListener {
closeActionMenu()
this@ChatActionMenu.context.executeAsync { feature(MediaDownloader::class).onMessageActionMenu(true) }
mediaDownloader.onMessageActionMenu(true)
}
})
@ -143,9 +146,13 @@ class ChatActionMenu : AbstractMenu() {
text = this@ChatActionMenu.context.translation["chat_action_menu.download_button"]
setOnClickListener {
closeActionMenu()
this@ChatActionMenu.context.executeAsync {
feature(MediaDownloader::class).onMessageActionMenu(false)
}
mediaDownloader.onMessageActionMenu(false)
}
setOnLongClickListener {
closeActionMenu()
context.vibrateLongPress()
mediaDownloader.onMessageActionMenu(isPreviewMode = false, forceAllowDuplicate = true)
true
}
})
}

View File

@ -13,6 +13,7 @@ import me.rhunk.snapenhance.core.ui.applyTheme
import me.rhunk.snapenhance.core.ui.menu.AbstractMenu
import me.rhunk.snapenhance.core.ui.triggerCloseTouchEvent
import me.rhunk.snapenhance.core.util.ktx.getId
import me.rhunk.snapenhance.core.util.ktx.vibrateLongPress
import me.rhunk.snapenhance.core.wrapper.impl.ScSize
import java.text.DateFormat
import java.util.Date
@ -128,14 +129,22 @@ class OperaContextActionMenu : AbstractMenu() {
}
}
linearLayout.addView(Button(view.context).apply {
text = translation["download"]
setOnClickListener {
mediaDownloader.downloadLastOperaMediaAsync()
parentView.triggerCloseTouchEvent()
}
applyTheme(isAmoled = false)
})
if (context.config.downloader.downloadContextMenu.get()) {
linearLayout.addView(Button(view.context).apply {
text = translation["download"]
setOnClickListener {
mediaDownloader.downloadLastOperaMediaAsync(allowDuplicate = false)
parentView.triggerCloseTouchEvent()
}
setOnLongClickListener {
context.vibrateLongPress()
mediaDownloader.downloadLastOperaMediaAsync(allowDuplicate = true)
parentView.triggerCloseTouchEvent()
true
}
applyTheme(isAmoled = false)
})
}
if (context.isDeveloper) {
linearLayout.addView(Button(view.context).apply {

View File

@ -11,6 +11,7 @@ import me.rhunk.snapenhance.core.ui.children
import me.rhunk.snapenhance.core.ui.menu.AbstractMenu
import me.rhunk.snapenhance.core.util.ktx.getDimens
import me.rhunk.snapenhance.core.util.ktx.getDrawable
import me.rhunk.snapenhance.core.util.ktx.vibrateLongPress
class OperaDownloadIconMenu : AbstractMenu() {
private val downloadSvgDrawable by lazy { context.resources.getDrawable("svg_download", context.androidContext.theme) }
@ -21,6 +22,8 @@ class OperaDownloadIconMenu : AbstractMenu() {
override fun inject(parent: ViewGroup, view: View, viewConsumer: (View) -> Unit) {
if (!context.config.downloader.operaDownloadButton.get()) return
val mediaDownloader = context.feature(MediaDownloader::class)
parent.addView(ImageView(view.context).apply {
setImageDrawable(downloadSvgDrawable)
setColorFilter(Color.WHITE)
@ -33,7 +36,12 @@ class OperaDownloadIconMenu : AbstractMenu() {
gravity = Gravity.TOP or Gravity.END
}
setOnClickListener {
this@OperaDownloadIconMenu.context.feature(MediaDownloader::class).downloadLastOperaMediaAsync()
mediaDownloader.downloadLastOperaMediaAsync(allowDuplicate = false)
}
setOnLongClickListener {
context.vibrateLongPress()
mediaDownloader.downloadLastOperaMediaAsync(allowDuplicate = true)
true
}
addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {

View File

@ -1,10 +1,13 @@
package me.rhunk.snapenhance.core.util.ktx
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.content.res.Resources.Theme
import android.content.res.TypedArray
import android.graphics.drawable.Drawable
import android.os.VibrationEffect
import android.os.Vibrator
import me.rhunk.snapenhance.common.Constants
@ -31,3 +34,7 @@ fun Resources.getStyledAttributes(name: String, theme: Theme): TypedArray {
fun Resources.getDrawable(name: String, theme: Theme): Drawable {
return getDrawable(getIdentifier(name, "drawable"), theme)
}
fun Context.vibrateLongPress() {
getSystemService(Vibrator::class.java).vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE))
}