commit 2e4b161eebeb229c732208da2f8faebc1b72f63f
parent bf73babf0d7101b474a8776ffd34ad4708f82ee5
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Wed, 13 Sep 2023 19:26:59 +0200

feat(media_downloader): better preview of chat messages
- set media resolver url to web api

Diffstat:
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/util/download/RemoteMediaResolver.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt | 4++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
3 files changed, 78 insertions(+), 23 deletions(-)

diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/util/download/RemoteMediaResolver.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/util/download/RemoteMediaResolver.kt @@ -9,7 +9,7 @@ import java.io.InputStream import java.util.Base64 object RemoteMediaResolver { - private const val BOLT_HTTP_RESOLVER_URL = "https://aws.api.snapchat.com/bolt-http" + private const val BOLT_HTTP_RESOLVER_URL = "https://web.snapchat.com/bolt-http" const val CF_ST_CDN_D = "https://cf-st.sc-cdn.net/d/" private val urlCache = mutableMapOf<String, String>() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt @@ -32,4 +32,8 @@ abstract class Feature( protected fun findClass(name: String): Class<*> { return context.androidContext.classLoader.loadClass(name) } + + protected fun runOnUiThread(block: () -> Unit) { + context.runOnUiThread(block) + } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt @@ -1,10 +1,18 @@ package me.rhunk.snapenhance.features.impl.downloader -import android.content.DialogInterface +import android.annotation.SuppressLint import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri +import android.view.Gravity +import android.view.ViewGroup.MarginLayoutParams +import android.view.Window import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.ProgressBar +import android.widget.TextView +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking import me.rhunk.snapenhance.bridge.DownloadCallback import me.rhunk.snapenhance.core.database.objects.FriendInfo @@ -475,6 +483,8 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp } } + @SuppressLint("SetTextI18n") + @OptIn(ExperimentalCoroutinesApi::class) fun downloadMessageId(messageId: Long, isPreview: Boolean = false) { val messageLogger = context.feature(MessageLogger::class) val message = context.database.getConversationMessageFromId(messageId) ?: throw Exception("Message not found in database") @@ -557,35 +567,76 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp return } - val downloadedMediaList = MediaDownloaderHelper.downloadMediaFromReference(urlProto) { - EncryptionHelper.decryptInputStream(it, contentType, messageReader, isArroyo = isArroyoMessage) - } + runBlocking { + val previewCoroutine = async { + val downloadedMediaList = MediaDownloaderHelper.downloadMediaFromReference(urlProto) { + EncryptionHelper.decryptInputStream(it, contentType, messageReader, isArroyo = isArroyoMessage) + } - runCatching { - val originalMedia = downloadedMediaList[SplitMediaAssetType.ORIGINAL] ?: return - val overlay = downloadedMediaList[SplitMediaAssetType.OVERLAY] + val originalMedia = downloadedMediaList[SplitMediaAssetType.ORIGINAL] ?: return@async null + val overlay = downloadedMediaList[SplitMediaAssetType.OVERLAY] - var bitmap: Bitmap? = PreviewUtils.createPreview(originalMedia, isVideo = FileType.fromByteArray(originalMedia).isVideo) + var bitmap: Bitmap? = PreviewUtils.createPreview(originalMedia, isVideo = FileType.fromByteArray(originalMedia).isVideo) - if (bitmap == null) { - context.shortToast(translations["failed_to_create_preview_toast"]) - return - } + if (bitmap == null) { + context.shortToast(translations["failed_to_create_preview_toast"]) + return@async null + } - overlay?.let { - bitmap = PreviewUtils.mergeBitmapOverlay(bitmap!!, BitmapFactory.decodeByteArray(it, 0, it.size)) + overlay?.also { + bitmap = PreviewUtils.mergeBitmapOverlay(bitmap!!, BitmapFactory.decodeByteArray(it, 0, it.size)) + } + + bitmap } with(ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity)) { - setView(ImageView(context).apply { - setImageBitmap(bitmap) - }) - setPositiveButton("Close") { dialog: DialogInterface, _: Int -> dialog.dismiss() } - this@MediaDownloader.context.runOnUiThread { show()} + val viewGroup = LinearLayout(context).apply { + layoutParams = MarginLayoutParams(MarginLayoutParams.MATCH_PARENT, MarginLayoutParams.MATCH_PARENT) + gravity = Gravity.CENTER_HORIZONTAL or Gravity.CENTER_VERTICAL + addView(ProgressBar(context).apply { + isIndeterminate = true + }) + } + + setOnDismissListener { + previewCoroutine.cancel() + } + + previewCoroutine.invokeOnCompletion { cause -> + runOnUiThread { + viewGroup.removeAllViews() + if (cause != null) { + viewGroup.addView(TextView(context).apply { + text = translations["failed_to_create_preview_toast"] + "\n" + cause.message + setPadding(30, 30, 30, 30) + }) + return@runOnUiThread + } + + viewGroup.addView(ImageView(context).apply { + setImageBitmap(previewCoroutine.getCompleted()) + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + ) + adjustViewBounds = true + }) + } + } + + runOnUiThread { + show().apply { + setContentView(viewGroup) + requestWindowFeature(Window.FEATURE_NO_TITLE) + window?.setLayout( + context.resources.displayMetrics.widthPixels, + context.resources.displayMetrics.heightPixels + ) + } + previewCoroutine.start() + } } - }.onFailure { - context.shortToast(translations["failed_to_create_preview_toast"]) - context.log.error("Failed to create preview", it) } }.onFailure { context.longToast(translations["failed_generic_toast"])