support for downloads in history

This commit is contained in:
Fabian Freund
2025-09-26 16:31:07 +02:00
parent 9a0c00f842
commit e20cb336b1
6 changed files with 128 additions and 10 deletions
@@ -45,6 +45,11 @@ object GlobalComponents {
.whenSessionsChange()
}
@DelicateCoroutinesApi
private fun restoreDownloads(newComponents: Components) = GlobalScope.launch(Dispatchers.Main) {
newComponents.useCases.downloadsUseCases.restoreDownloads()
}
@OptIn(DelicateCoroutinesApi::class)
fun setUp(
applicationContext: Context,
@@ -82,8 +87,7 @@ object GlobalComponents {
newComponents.core.engine.warmUp()
restoreBrowserState(newComponents)
//newComponents.useCases.downloadsUseCases.restoreDownloads()
restoreDownloads(newComponents)
try {
GlobalPlacesDependencyProvider.initialize(newComponents.core.historyStorage)
@@ -1,7 +1,6 @@
package eu.weblibre.flutter_mozilla_components.api
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import eu.weblibre.flutter_mozilla_components.pigeons.AutocompleteResult
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoHistoryApi
import eu.weblibre.flutter_mozilla_components.pigeons.VisitInfo
import eu.weblibre.flutter_mozilla_components.pigeons.VisitType
@@ -10,6 +9,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.browser.state.state.content.DownloadState
import kotlin.time.Duration.Companion.milliseconds
class GeckoHistoryApiImpl() : GeckoHistoryApi {
companion object {
@@ -20,6 +21,32 @@ class GeckoHistoryApiImpl() : GeckoHistoryApi {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
private fun Map<String, DownloadState>.toVisitInfoList(
startMillis: Long,
endMillis: Long,
): List<VisitInfo> =
values
.filter { isDisplayableItem(it.status) &&
it.createdTime >= startMillis && it.createdTime <= endMillis
}
.distinctBy { Pair(it.fileName, it.status) }
.sortedByDescending { it.createdTime } // sort from newest to oldest
.map { it.toVisitInfo() }
private fun isDisplayableItem(status: DownloadState.Status) =
status != DownloadState.Status.CANCELLED
private fun DownloadState.toVisitInfo() =
VisitInfo(
url = url,
visitType = VisitType.DOWNLOAD,
visitTime = createdTime,
title = filePath,
previewImageUrl = contentType,
isRemote = false,
contentId = id
)
override fun getDetailedVisits(
startMillis: Long,
endMillis: Long,
@@ -28,7 +55,7 @@ class GeckoHistoryApiImpl() : GeckoHistoryApi {
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
val visits = components.core.historyStorage.getDetailedVisits(
var visits = components.core.historyStorage.getDetailedVisits(
startMillis,
endMillis,
excludeTypes.map {
@@ -43,9 +70,7 @@ class GeckoHistoryApiImpl() : GeckoHistoryApi {
VisitType.FRAMED_LINK -> mozilla.components.concept.storage.VisitType.FRAMED_LINK
VisitType.RELOAD -> mozilla.components.concept.storage.VisitType.RELOAD
}
})
callback(Result.success(visits.map {
}).map {
VisitInfo(
url = it.url,
title = it.title,
@@ -65,6 +90,12 @@ class GeckoHistoryApiImpl() : GeckoHistoryApi {
isRemote = it.isRemote
)
}
if (!excludeTypes.contains(VisitType.DOWNLOAD)) {
visits = visits + components.core.store.state.downloads.toVisitInfoList(startMillis, endMillis)
}
callback(Result.success(visits
))
}
}
@@ -84,6 +115,19 @@ class GeckoHistoryApiImpl() : GeckoHistoryApi {
}
}
override fun deleteDownload(
id: String,
callback: (Result<Unit>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.useCases.downloadsUseCases.removeDownload(id)
callback(Result.success(Unit))
}
}
}
override fun deleteVisitsBetween(
startMillis: Long,
endMillis: Long,
@@ -1146,7 +1146,8 @@ data class VisitInfo (
val visitTime: Long,
val visitType: VisitType,
val previewImageUrl: String? = null,
val isRemote: Boolean
val isRemote: Boolean,
val contentId: String? = null
)
{
companion object {
@@ -1157,7 +1158,8 @@ data class VisitInfo (
val visitType = pigeonVar_list[3] as VisitType
val previewImageUrl = pigeonVar_list[4] as String?
val isRemote = pigeonVar_list[5] as Boolean
return VisitInfo(url, title, visitTime, visitType, previewImageUrl, isRemote)
val contentId = pigeonVar_list[6] as String?
return VisitInfo(url, title, visitTime, visitType, previewImageUrl, isRemote, contentId)
}
}
fun toList(): List<Any?> {
@@ -1168,6 +1170,7 @@ data class VisitInfo (
visitType,
previewImageUrl,
isRemote,
contentId,
)
}
override fun equals(other: Any?): Boolean {
@@ -4767,6 +4770,7 @@ interface GeckoDeleteBrowsingDataController {
interface GeckoHistoryApi {
fun getDetailedVisits(startMillis: Long, endMillis: Long, excludeTypes: List<VisitType>, callback: (Result<List<VisitInfo>>) -> Unit)
fun deleteVisit(url: String, timestamp: Long, callback: (Result<Unit>) -> Unit)
fun deleteDownload(id: String, callback: (Result<Unit>) -> Unit)
fun deleteVisitsBetween(startMillis: Long, endMillis: Long, callback: (Result<Unit>) -> Unit)
companion object {
@@ -4820,6 +4824,25 @@ interface GeckoHistoryApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteDownload$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val idArg = args[0] as String
api.deleteDownload(idArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
reply.reply(GeckoPigeonUtils.wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteVisitsBetween$separatedMessageChannelSuffix", codec)
if (api != null) {