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) {
@@ -26,7 +26,20 @@ class GeckoHistoryService {
}
Future<void> deleteVisit(VisitInfo info) {
return _api.deleteVisit(info.url, info.visitTime);
switch (info.visitType) {
case VisitType.link:
case VisitType.typed:
case VisitType.embed:
case VisitType.redirectPermanent:
case VisitType.redirectTemporary:
case VisitType.framedLink:
case VisitType.reload:
return _api.deleteVisit(info.url, info.visitTime);
case VisitType.download:
return _api.deleteDownload(info.contentId!);
case VisitType.bookmark:
throw UnimplementedError();
}
}
Future<void> deleteVisitsBetween(DateTime start, DateTime end) {
@@ -1257,6 +1257,7 @@ class VisitInfo {
required this.visitType,
this.previewImageUrl,
required this.isRemote,
this.contentId,
});
String url;
@@ -1271,6 +1272,8 @@ class VisitInfo {
bool isRemote;
String? contentId;
List<Object?> _toList() {
return <Object?>[
url,
@@ -1279,6 +1282,7 @@ class VisitInfo {
visitType,
previewImageUrl,
isRemote,
contentId,
];
}
@@ -1294,6 +1298,7 @@ class VisitInfo {
visitType: result[3]! as VisitType,
previewImageUrl: result[4] as String?,
isRemote: result[5]! as bool,
contentId: result[6] as String?,
);
}
@@ -5783,6 +5788,29 @@ class GeckoHistoryApi {
}
}
Future<void> deleteDownload(String id) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteDownload$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[id]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}
Future<void> deleteVisitsBetween(int startMillis, int endMillis) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteVisitsBetween$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
@@ -425,6 +425,8 @@ class VisitInfo {
final String? previewImageUrl;
final bool isRemote;
final String? contentId;
VisitInfo(
this.url,
this.title,
@@ -432,6 +434,7 @@ class VisitInfo {
this.visitType,
this.previewImageUrl,
this.isRemote,
this.contentId,
);
}
@@ -1253,6 +1256,9 @@ abstract class GeckoHistoryApi {
@async
void deleteVisit(String url, int timestamp);
@async
void deleteDownload(String id);
@async
void deleteVisitsBetween(int startMillis, int endMillis);
}