feat: download section

This commit is contained in:
rhunk
2023-08-05 19:34:31 +02:00
parent e6e75123f8
commit 7b6b0bfd70
18 changed files with 386 additions and 93 deletions

View File

@ -4,15 +4,17 @@ import android.annotation.SuppressLint
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import me.rhunk.snapenhance.download.data.DownloadMetadata
import me.rhunk.snapenhance.download.data.PendingDownload
import me.rhunk.snapenhance.download.data.DownloadObject
import me.rhunk.snapenhance.download.data.DownloadStage
import me.rhunk.snapenhance.ui.download.MediaFilter
import me.rhunk.snapenhance.util.SQLiteDatabaseHelper
import me.rhunk.snapenhance.util.getIntOrNull
import me.rhunk.snapenhance.util.getStringOrNull
class DownloadTaskManager {
private lateinit var taskDatabase: SQLiteDatabase
private val pendingTasks = mutableMapOf<Int, PendingDownload>()
private val cachedTasks = mutableMapOf<Int, PendingDownload>()
private val pendingTasks = mutableMapOf<Int, DownloadObject>()
private val cachedTasks = mutableMapOf<Int, DownloadObject>()
@SuppressLint("Range")
fun init(context: Context) {
@ -33,7 +35,7 @@ class DownloadTaskManager {
}
}
fun addTask(task: PendingDownload): Int {
fun addTask(task: DownloadObject): Int {
taskDatabase.execSQL("INSERT INTO tasks (hash, outputPath, outputFile, mediaDisplayType, mediaDisplaySource, iconUrl, downloadStage) VALUES (?, ?, ?, ?, ?, ?, ?)",
arrayOf(
task.metadata.mediaIdentifier,
@ -53,7 +55,7 @@ class DownloadTaskManager {
return task.downloadId
}
fun updateTask(task: PendingDownload) {
fun updateTask(task: DownloadObject) {
taskDatabase.execSQL("UPDATE tasks SET hash = ?, outputPath = ?, outputFile = ?, mediaDisplayType = ?, mediaDisplaySource = ?, iconUrl = ?, downloadStage = ? WHERE id = ?",
arrayOf(
task.metadata.mediaIdentifier,
@ -107,13 +109,13 @@ class DownloadTaskManager {
pendingTasks.remove(id)
}
fun removeTask(task: PendingDownload) {
fun removeTask(task: DownloadObject) {
removeTask(task.downloadId)
}
fun queryAllTasks(filter: MediaFilter): Map<Int, PendingDownload> {
fun queryFirstTasks(filter: MediaFilter): Map<Int, DownloadObject> {
val isPendingFilter = filter == MediaFilter.PENDING
val tasks = mutableMapOf<Int, PendingDownload>()
val tasks = mutableMapOf<Int, DownloadObject>()
tasks.putAll(pendingTasks.filter { isPendingFilter || filter.matches(it.value.metadata.mediaDisplayType) })
if (isPendingFilter) {
@ -130,7 +132,7 @@ class DownloadTaskManager {
}
@SuppressLint("Range")
fun queryTasks(from: Int, amount: Int = 20, filter: MediaFilter = MediaFilter.NONE): Map<Int, PendingDownload> {
fun queryTasks(from: Int, amount: Int = 30, filter: MediaFilter = MediaFilter.NONE): Map<Int, DownloadObject> {
if (filter == MediaFilter.PENDING) {
return emptyMap()
}
@ -139,27 +141,27 @@ class DownloadTaskManager {
"SELECT * FROM tasks WHERE id < ? AND mediaDisplayType LIKE ? ORDER BY id DESC LIMIT ?",
arrayOf(
from.toString(),
filter.mediaDisplayType.let { if (it == null) "%" else "%$it" },
if (filter.shouldIgnoreFilter) "%" else "%${filter.key}",
amount.toString()
)
)
val result = sortedMapOf<Int, PendingDownload>()
val result = sortedMapOf<Int, DownloadObject>()
while (cursor.moveToNext()) {
val task = PendingDownload(
downloadId = cursor.getInt(cursor.getColumnIndex("id")),
outputFile = cursor.getString(cursor.getColumnIndex("outputFile")),
val task = DownloadObject(
downloadId = cursor.getIntOrNull("id")!!,
outputFile = cursor.getStringOrNull("outputFile"),
metadata = DownloadMetadata(
outputPath = cursor.getString(cursor.getColumnIndex("outputPath")),
mediaIdentifier = cursor.getString(cursor.getColumnIndex("hash")),
mediaDisplayType = cursor.getString(cursor.getColumnIndex("mediaDisplayType")),
mediaDisplaySource = cursor.getString(cursor.getColumnIndex("mediaDisplaySource")),
iconUrl = cursor.getString(cursor.getColumnIndex("iconUrl"))
outputPath = cursor.getStringOrNull("outputPath")!!,
mediaIdentifier = cursor.getStringOrNull("hash"),
mediaDisplayType = cursor.getStringOrNull("mediaDisplayType"),
mediaDisplaySource = cursor.getStringOrNull("mediaDisplaySource"),
iconUrl = cursor.getStringOrNull("iconUrl")
)
).apply {
downloadTaskManager = this@DownloadTaskManager
downloadStage = DownloadStage.valueOf(cursor.getString(cursor.getColumnIndex("downloadStage")))
downloadStage = DownloadStage.valueOf(cursor.getStringOrNull("downloadStage")!!)
//if downloadStage is not saved, it means the app was killed before the download was finished
if (downloadStage != DownloadStage.SAVED) {
downloadStage = DownloadStage.FAILED

View File

@ -1,10 +1,9 @@
package me.rhunk.snapenhance.download.data
import kotlinx.coroutines.Job
import me.rhunk.snapenhance.SharedContext
import me.rhunk.snapenhance.download.DownloadTaskManager
data class PendingDownload(
data class DownloadObject(
var downloadId: Int = 0,
var outputFile: String? = null,
val metadata : DownloadMetadata

View File

@ -242,7 +242,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
pathSuffix = authorUsername,
mediaIdentifier = "$conversationId$senderId${conversationMessage.server_message_id}",
mediaDisplaySource = authorUsername,
mediaDisplayType = MediaFilter.CHAT_MEDIA.mediaDisplayType,
mediaDisplayType = MediaFilter.CHAT_MEDIA.key,
friendInfo = author
), mediaInfoMap)
@ -282,7 +282,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
pathSuffix = authorName,
mediaIdentifier = paramMap["MEDIA_ID"].toString(),
mediaDisplaySource = authorName,
mediaDisplayType = MediaFilter.STORY.mediaDisplayType,
mediaDisplayType = MediaFilter.STORY.key,
friendInfo = author
), mediaInfoMap)
return
@ -311,7 +311,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
downloadOperaMedia(provideDownloadManagerClient(
pathSuffix = "Spotlight",
mediaIdentifier = paramMap["SNAP_ID"].toString(),
mediaDisplayType = MediaFilter.SPOTLIGHT.mediaDisplayType,
mediaDisplayType = MediaFilter.SPOTLIGHT.key,
mediaDisplaySource = paramMap["TIME_STAMP"].toString()
), mediaInfoMap)
return
@ -476,7 +476,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
pathSuffix = authorName,
mediaIdentifier = "${message.client_conversation_id}${message.sender_id}${message.server_message_id}",
mediaDisplaySource = authorName,
mediaDisplayType = MediaFilter.CHAT_MEDIA.mediaDisplayType,
mediaDisplayType = MediaFilter.CHAT_MEDIA.key,
friendInfo = friendInfo
).downloadSingleMedia(
Base64.UrlSafe.encode(urlProto),

View File

@ -26,7 +26,7 @@ import me.rhunk.snapenhance.Logger
import me.rhunk.snapenhance.SharedContext
import me.rhunk.snapenhance.core.R
import me.rhunk.snapenhance.data.FileType
import me.rhunk.snapenhance.download.data.PendingDownload
import me.rhunk.snapenhance.download.data.DownloadObject
import me.rhunk.snapenhance.download.data.DownloadStage
import me.rhunk.snapenhance.util.snap.PreviewUtils
import java.io.File
@ -37,7 +37,7 @@ import kotlin.coroutines.coroutineContext
class DownloadListAdapter(
private val activity: DownloadManagerActivity,
private val downloadList: MutableList<PendingDownload>
private val downloadList: MutableList<DownloadObject>
): Adapter<DownloadListAdapter.ViewHolder>() {
private val coroutineScope = CoroutineScope(Dispatchers.IO)
private val previewJobs = mutableMapOf<Int, Job>()
@ -68,7 +68,7 @@ class DownloadListAdapter(
}
@SuppressLint("Recycle")
private suspend fun handlePreview(download: PendingDownload, holder: ViewHolder) {
private suspend fun handlePreview(download: DownloadObject, holder: ViewHolder) {
download.outputFile?.let {
val uri = Uri.parse(it)
runCatching {
@ -124,7 +124,7 @@ class DownloadListAdapter(
}
}
private fun updateViewHolder(download: PendingDownload, holder: ViewHolder) {
private fun updateViewHolder(download: DownloadObject, holder: ViewHolder) {
holder.status.text = download.downloadStage.toString()
holder.view.background = holder.view.context.getDrawable(R.drawable.download_manager_item_background)
@ -163,7 +163,7 @@ class DownloadListAdapter(
}
}
holder.bitmojiIcon.setImageResource(R.drawable.bitmoji_blank)
// holder.bitmojiIcon.setImageResource(R.drawable.bitmoji_blank)
pendingDownload.metadata.iconUrl?.let { url ->
thread(start = true) {

View File

@ -19,13 +19,13 @@ import me.rhunk.snapenhance.SharedContext
import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper
import me.rhunk.snapenhance.core.BuildConfig
import me.rhunk.snapenhance.core.R
import me.rhunk.snapenhance.download.data.PendingDownload
import me.rhunk.snapenhance.download.data.DownloadObject
class DownloadManagerActivity : Activity() {
lateinit var translation: LocaleWrapper
private val backCallbacks = mutableListOf<() -> Unit>()
private val fetchedDownloadTasks = mutableListOf<PendingDownload>()
private val fetchedDownloadTasks = mutableListOf<DownloadObject>()
private var listFilter = MediaFilter.NONE
private val preferences by lazy {
@ -42,7 +42,7 @@ class DownloadManagerActivity : Activity() {
@SuppressLint("NotifyDataSetChanged")
private fun updateListContent() {
fetchedDownloadTasks.clear()
fetchedDownloadTasks.addAll(SharedContext.downloadTaskManager.queryAllTasks(filter = listFilter).values)
fetchedDownloadTasks.addAll(SharedContext.downloadTaskManager.queryFirstTasks(filter = listFilter).values)
with(findViewById<RecyclerView>(R.id.download_list)) {
adapter?.notifyDataSetChanged()

View File

@ -1,17 +1,17 @@
package me.rhunk.snapenhance.ui.download
enum class MediaFilter(
val mediaDisplayType: String? = null
val key: String,
val shouldIgnoreFilter: Boolean = false
) {
NONE,
PENDING,
CHAT_MEDIA("Chat Media"),
STORY("Story"),
SPOTLIGHT("Spotlight");
NONE("none", true),
PENDING("pending", true),
CHAT_MEDIA("chat_media"),
STORY("story"),
SPOTLIGHT("spotlight");
fun matches(source: String?): Boolean {
if (mediaDisplayType == null) return true
if (source == null) return false
return source.contains(mediaDisplayType, ignoreCase = true)
return source.contains(key, ignoreCase = true)
}
}

View File

@ -48,9 +48,7 @@ object PreviewUtils {
setDataSource(file.absolutePath)
}.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)
} else {
BitmapFactory.decodeFile(file.absolutePath, BitmapFactory.Options().apply {
inSampleSize = 1
})
BitmapFactory.decodeFile(file.absolutePath, BitmapFactory.Options())
}
}

View File

@ -1,11 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90"
android:viewportHeight="90">
<path
android:pathData="M45,90.1c10.8,0 20.8,-3.8 28.6,-10.2c-1.4,-2.1 -3,-3.6 -4.7,-5c-5.2,-4.1 -12.6,-5.6 -17.7,-6.5l-0.2,-2c7.8,-4.6 9.7,-9.5 12.8,-19.8l0.1,-0.7c0,0 2.7,-1.1 3.1,-6.1c0.6,-6.8 -2.2,-4.8 -2.2,-5.3C65.1,31 65,26.4 64,23c-2.1,-7.3 -9.2,-13.1 -19,-13.1S28.1,15.6 26,23c-1,3.4 -1.1,8 -0.8,11.6c0,0.5 -2.7,-1.5 -2.2,5.3c0.4,5 3.1,6.1 3.1,6.1l0.1,0.7c3.1,10.3 5,15.2 12.8,19.8l-0.2,2c-5,0.9 -12.5,2.4 -17.7,6.5c-1.7,1.4 -3.3,2.9 -4.7,5C24.2,86.3 34.2,90.1 45,90.1z"
android:strokeWidth="1.5"
android:fillColor="#979797"
android:strokeColor="#000000"/>
</vector>