Context: Android 10, API 29 .
I print a PDF file generated from a WebView
, but now I’d like to save it to a file. So I tried the Intent.ACTION_CREATE_DOCUMENT
to pick the file and save it via the printAdapter
's onWrite
method.
The problem is that the file is always empty - 0 bytes - and no errors are raised. It justs calls onWriteFailed
, but with an empty error message.
choosenFileUri
has a value like content://com.android.providers.downloads.documents/document/37
The method I use to start the intent to pick a new file. Note that the result of this activity is a Uri
:
fun startIntentToCreatePdfFile(fragment: Fragment, filename : String) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
putExtra(Intent.EXTRA_TITLE, filename)
}
fragment.startActivityForResult(intent, IntentCreatePdfDocument)
}
The method I use to “print” the PDF to a file. The fileUri
comes from the Intent.ACTION_CREATE_DOCUMENT
:
fun printPdfToFile(
context: Context,
webView: WebView,
fileUri: Uri
) {
(context.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let {
val jobName = "Print PDF to save it"
val printAdapter = webView.createPrintDocumentAdapter(jobName)
val printAttributes = PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(PrintAttributes.Resolution("pdf", "pdf", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS).build()
printAdapter.onLayout(null, printAttributes, null, object : LayoutResultCallback() {
override fun onLayoutFinished(info: PrintDocumentInfo, changed: Boolean) {
context.contentResolver.openFileDescriptor(fileUri, "w")?.use {
printAdapter.onWrite(
arrayOf(PageRange.ALL_PAGES),
it,
CancellationSignal(),
object : WriteResultCallback() {
})
}
}
}, null)
}
}
What I do pick file onActivityResult
:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != Activity.RESULT_OK) {
return null
}
if (requestCode != IntentCreatePdfDocument) {
throw Exception("RequestCode not implemented: $requestCode")
}
val choosenFileUri = data?.data
// If it is null, nothing to do
if (choosenFileUri == null) {
return
}
try {
HtmlHelpers.savePdfFromHtml(
requireContext(),
"html document to be represented in the WebView",
choosenFileUri)
} catch (exception: Exception) {
_logger.error(exception)
Helpers.showError(requireActivity(), getString(R.string.generic_error))
}
dismiss()
}
…where HtmlHelpers.savePdfFromHtml
is:
fun savePdfFromHtml(
context: Context,
htmlContent: String,
fileUri: Uri
) {
generatePdfFromHtml(
context,
htmlContent
) { webView ->
PrintHelpers.printPdfToFile(
context,
webView,
fileUri)
}
}
…and generatePdfFromHtml
is:
private fun generatePdfFromHtml(
context: Context,
htmlContent: String,
onPdfCreated: (webView: WebView) -> Unit
) {
val webView = WebView(context)
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(webView: WebView, url: String) {
onPdfCreated(webView)
}
}
webView.loadDataWithBaseURL(
null,
htmlContent,
"text/html; charset=utf-8",
"UTF-8",
null);
}
I checked all the other answer about this topic, but everyone creates manually the ParcelFileDescriptor
instead of it
in the onWrite
method. Everyone does something like this:
fun getOutputFile(path: File, fileName: String): ParcelFileDescriptor? {
val file = File(path, fileName)
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)
}
But I cannot do this since I have only the Uri.
Can you please help me?
Thanks!