improve download handling

This commit is contained in:
Fabian Freund
2026-05-26 06:23:31 +02:00
parent 038cb3dffc
commit da90d07318
13 changed files with 3803 additions and 2425 deletions
@@ -26,7 +26,9 @@ import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import eu.weblibre.flutter_mozilla_components.addons.WebExtensionPromptFeature
import eu.weblibre.flutter_mozilla_components.databinding.FragmentBrowserBinding
import eu.weblibre.flutter_mozilla_components.ext.EventSequence
import eu.weblibre.flutter_mozilla_components.ext.getPreferenceKey
import eu.weblibre.flutter_mozilla_components.ext.toPigeonDownloadState
import eu.weblibre.flutter_mozilla_components.feature.BrowserHandlingScrollFeature
import eu.weblibre.flutter_mozilla_components.feature.KeyboardVisibilityFeature
import eu.weblibre.flutter_mozilla_components.feature.ReadabilityExtractFeature
@@ -320,6 +322,15 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
fragmentManager = childFragmentManager,
onDownloadStopped = { download, id, status ->
Logger.debug("Download done. ID#$id $download with status $status")
if (
status == mozilla.components.browser.state.state.content.DownloadState.Status.COMPLETED ||
status == mozilla.components.browser.state.state.content.DownloadState.Status.FAILED
) {
components.flutterEvents.onDownloadStopped(
EventSequence.next(),
download.toPigeonDownloadState(status),
) { _ -> }
}
},
downloadFileUtils = DefaultDownloadFileUtils(
context = components.profileApplicationContext,
@@ -15,6 +15,7 @@ import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.action.CopyInternetResourceAction
import mozilla.components.browser.state.action.ShareResourceAction
import mozilla.components.browser.state.state.content.ShareResourceState
import mozilla.components.support.utils.DefaultDownloadFileUtils
import java.util.UUID
class GeckoDownloadsApiImpl : GeckoDownloadsApi {
@@ -39,6 +40,23 @@ class GeckoDownloadsApiImpl : GeckoDownloadsApi {
components.core.store.dispatch(ShareResourceAction.AddShareAction(tabId, state.toMozillaShareInternetResourceState()))
}
override fun openDownloadedFile(
fileName: String,
directoryPath: String,
contentType: String?,
): Boolean {
return DefaultDownloadFileUtils(
context = components.profileApplicationContext,
downloadLocation = {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
},
).openFile(
fileName = fileName,
directoryPath = directoryPath,
contentType = contentType,
)
}
private fun ShareInternetResourceState.toMozillaShareInternetResourceState(): ShareResourceState.InternetResource {
return ShareResourceState.InternetResource(
url = url,
@@ -84,4 +102,4 @@ class GeckoDownloadsApiImpl : GeckoDownloadsApi {
}
}
}
}
@@ -0,0 +1,44 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package eu.weblibre.flutter_mozilla_components.ext
import eu.weblibre.flutter_mozilla_components.pigeons.DownloadState as PigeonDownloadState
import eu.weblibre.flutter_mozilla_components.pigeons.DownloadStatus as PigeonDownloadStatus
import mozilla.components.browser.state.state.content.DownloadState as MozillaDownloadState
fun MozillaDownloadState.toPigeonDownloadState(
statusOverride: MozillaDownloadState.Status = status,
): PigeonDownloadState =
PigeonDownloadState(
url = url,
fileName = fileName,
contentType = contentType,
contentLength = contentLength,
currentBytesCopied = currentBytesCopied,
status = statusOverride.toPigeonDownloadStatus(),
userAgent = userAgent,
destinationDirectory = null,
directoryPath = directoryPath,
referrerUrl = referrerUrl,
skipConfirmation = skipConfirmation,
openInApp = openInApp,
id = id,
sessionId = sessionId,
private = private,
createdTime = createdTime,
notificationId = notificationId?.toLong(),
)
private fun MozillaDownloadState.Status.toPigeonDownloadStatus(): PigeonDownloadStatus =
when (this) {
MozillaDownloadState.Status.INITIATED -> PigeonDownloadStatus.INITIATED
MozillaDownloadState.Status.DOWNLOADING -> PigeonDownloadStatus.DOWNLOADING
MozillaDownloadState.Status.PAUSED -> PigeonDownloadStatus.PAUSED
MozillaDownloadState.Status.CANCELLED -> PigeonDownloadStatus.CANCELLED
MozillaDownloadState.Status.FAILED -> PigeonDownloadStatus.FAILED
MozillaDownloadState.Status.COMPLETED -> PigeonDownloadStatus.COMPLETED
}
@@ -9046,6 +9046,23 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onDownloadStopped(sequenceArg: Long, stateArg: DownloadState, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onDownloadStopped$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(sequenceArg, stateArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(GeckoPigeonUtils.createConnectionError(channelName)))
}
}
}
fun onManifestUpdate(sequenceArg: Long, tabIdArg: String, manifestArg: PwaManifest?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
@@ -10537,6 +10554,7 @@ interface GeckoDownloadsApi {
fun requestDownload(tabId: String, state: DownloadState)
fun copyInternetResource(tabId: String, state: ShareInternetResourceState)
fun shareInternetResource(tabId: String, state: ShareInternetResourceState)
fun openDownloadedFile(fileName: String, directoryPath: String, contentType: String?): Boolean
companion object {
/** The codec used by GeckoDownloadsApi. */
@@ -10604,6 +10622,25 @@ interface GeckoDownloadsApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoDownloadsApi.openDownloadedFile$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val fileNameArg = args[0] as String
val directoryPathArg = args[1] as String
val contentTypeArg = args[2] as String?
val wrapped: List<Any?> = try {
listOf(api.openDownloadedFile(fileNameArg, directoryPathArg, contentTypeArg))
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}