hotwired / turbo-android

Android framework for making Turbo native apps
MIT License
424 stars 50 forks source link

Download a pdf or other file from url #265

Closed Ddam closed 1 year ago

Ddam commented 1 year ago

I've been trying to figure out how to download a file from a URL on my web application for several days. When I click on the link I get "Error loading page". Is there a solution for this type of action? I tried to redirect to browser with an intent "ACTION_VIEW" but my application is subject to authentication, I am redirected to login page and not to desired file.

svantepolk commented 1 year ago

I ended up using DownloadManager in this situation. DownloadManager.Request allows you to add headers; that's where you can add your application's cookie, which you can get via CookieManager.

You can use a BroadcastReceiver to automatically view the file once it's downloaded.

breadluvr commented 1 year ago

I'm using this lib for viewing PDFs in our application: https://github.com/barteksc/AndroidPdfViewer

It works well – we did have some issues with saving PDFs directly on a device but I think that is because the download path had changed between versions, so I removed it and am just using the ShareCompat to provide a way to download PDFs.

Ddam commented 1 year ago

Thanks @svantepolk, I finally found the solution with DownloadManager. I give code as example if it helps.

// In WebFragment.kt file

override fun shouldNavigateTo(newLocation: String): Boolean {
    return if (isDownloadUrl(newLocation)) {
        startDownload(newLocation)

        false
    } else {
        super<NavDestination>.shouldNavigateTo(newLocation)
    }
}

private fun startDownload(location: String) {
    val source: Uri = Uri.parse(location)
    val request: DownloadManager.Request = DownloadManager.Request(source)
    val cookie: String = CookieManager.getInstance().getCookie(location)
    val downloadManager = activity?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

    request.addRequestHeader("Set-Cookie", cookie)
    request.addRequestHeader("User-Agent", USER_AGENT)
    request.addRequestHeader("Accept", "text/html, application/xhtml+xml, *" + "/" + "*")
    request.addRequestHeader("Accept-Language", "en-US,en;q=0.7,he;q=0.3")
    request.addRequestHeader("Referer", location)

    val destinationDir = File(Environment.DIRECTORY_DOWNLOADS)

    if (destinationDir != null) {
        if (!destinationDir.exists()) {
            destinationDir.mkdir() // Don't forget to make the directory if it's not there
        }
    }

    val destinationFile = source.lastPathSegment?.let { File(destinationDir, it) }
    val extFile = destinationFile?.extension

    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, source.lastPathSegment?: "new_file.$extFile")

    downloadManager.enqueue(request)
}

private fun isDownloadUrl(url: String): Boolean {
    val fileExtension = MimeTypeMap.getFileExtensionFromUrl(url)
    val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension)

    return mimeType != null && mimeType.startsWith("application")
}

@astroblemeal, I will look at this solution because I am interested in viewing PDFs too. Thanks.

svantepolk commented 1 year ago

You should be able to use ACTION_VIEW to open PDFs as well. Though, it will open it in an external application, which might not be the UX you're after.