I'm trying to display in the pdf intent of android an file using his Uri, but it seems that I always get an blank pdf and don't know why, can someone give me some advice why.
Code:
private fun pdfConverter(pdfFile: DtoSymptomCheckerPdf?) {
val documentsPath = File(context?.filesDir, "documents")
if(!documentsPath.exists()){
documentsPath.mkdir()
}
val file = File(documentsPath, pdfFile?.filename)
val pdfAsBytes: ByteArray = android.util.Base64.decode(pdfFile?.file, 0)
val os = FileOutputStream(file,false)
os.write(pdfAsBytes)
os.flush()
os.close()
println("EL CONTENT: " + file.length())
val uri = FileProvider.getUriForFile(App.applicationContext,
BuildConfig.APPLICATION_ID + ".provider", file)
val pdfIntent = Intent(Intent.ACTION_VIEW)
pdfIntent.setDataAndType(uri, "application/pdf")
pdfIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
try {
startActivity(pdfIntent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(requireContext(), "Can't read pdf file",
Toast.LENGTH_SHORT).show()
}
}
Related
I'm trying to write a byteArray received from a server. This is my code
private fun writePdf(content: ByteArray) {
val storageDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
val file = File("${storageDir?.path}/", "${Date().time}Download.pdf")
try {
// file.writeBytes(archivo)
val os = FileOutputStream(file, false)
os.write(content)
os.flush()
os.close()
} catch (e: IOException) {
e.printStackTrace()
}
val intent = Intent(Intent.ACTION_VIEW)
val uri = FileProvider
.getUriForFile(
this,
this.packageName + ".fileprovider",
file)
intent.setDataAndType(uri, "application/pdf")
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
try{
startActivity(intent)
}catch (e: Exception){
e.printStackTrace()
Toast.makeText(this, "Error", Toast.LENGTH_LONG)
.show()
}
}
The problem is that when the pdf opens it is blank, like nothing has been written.
I've tried writing with FileOutputStream and File.writeBytes.
I've checked the byteArray (in case is corrupted or something) and it has no problems.
I've checked the length() of the file before and after writing, and it's length increases accordingly.
Thanks, any kind of help is appreciated.
I finally found the problem, and solution. Everything with the code above was okay; the problem was that the flags for the intent to visualize the PDF were wrong. Instead of:
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
It should be:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
In my app, I have several file Uri (NOT URI) store. These are of the form:
content://com.android.providers.downloads.documents/document/427
My intention is, once I click a button, open one of these Uri in a file reader (may it be MS Word, Excel, PDF Reader... depending on the extension and device).
I am currently trying this snippet:
val file = File(Uri.parse(uri).path!!)
val myMime: MimeTypeMap = MimeTypeMap.getSingleton()
val newIntent = Intent(Intent.ACTION_VIEW)
val mimeType: String = myMime.getMimeTypeFromExtension(file.extension).toString()
newIntent.setDataAndType(
FileProvider.getUriForFile(applicationContext, "${applicationContext.packageName}.provider", file), mimeType)
newIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
newIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
try {
startActivity(newIntent)
} catch (e: ActivityNotFoundException) {
}
But every time I try to open it, I get:
java.lang.IllegalArgumentException: Failed to find configured root that contains /document/427
Could you please tell me what am I missing? No answers I've found here targeted my problem. Thanks in advance!
UPDATE
Thanks to #CommonsWare help, I managed to avoid an Exception, my code now looks like this:
val newIntent = Intent(Intent.ACTION_VIEW)
newIntent.addCategory(Intent.CATEGORY_OPENABLE)
newIntent.data = Uri.parse(uri)
newIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
newIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
try {
startActivity(newIntent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
}
I'm getting the Uri from here:
val fileResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
viewModel.uploadFile(result, false)
}
fileFab.setOnClickListener {
val intent = Intent()
intent.type = "application/*"
intent.action = Intent.ACTION_OPEN_DOCUMENT
fileResultLauncher.launch(intent)
}
Unfortunately, I'm getting a NoActivityFoundException
Get rid of:
val file = File(Uri.parse(uri).path!!)
...and replace your setDataAndType() call with:
newIntent.setDataAndType(Uri.parse(uri), mimeType)
Iam trying to open a saved pdf file from the internal cache dir.
In the "old" way my code looked liked this.
In jetpack compose i can use this part of code, the pdf is beeining created and i can see it in the device explorer. But how can i display the pdf on screen ?
fun decodeTestPdfString(pdf_string:String, context:Context) {
//make FileOutPutStream
var fos: FileOutputStream? = null
try {
if (pdf_string != null) {
f = File(context?.cacheDir, "testFile" + ".pdf")
f!!.createNewFile()
fos = FileOutputStream(f)
val decodedString: ByteArray = Base64.decode(pdf_string, Base64.DEFAULT)
fos.write(decodedString)
fos.flush()
fos.close()
}
} catch (e: Exception) {
} finally {
if (fos != null) {
fos = null
}
}
val path: Uri = FileProvider.getUriForFile(context!!, context!!.getApplicationContext().getPackageName() + ".provider", f!!)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(path, "application/pdf")
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
}
}
}
But the last step (try intent) is giving an issue.
The file is created in the cache dir, i can see it.
To open the pdf reader:
startActivity(context, intent, options:null)
I'm trying to find a solution to simply download a base64 encoded pdf file of a webservice and open it with an preinstalled pdf viewer. My application targets Android R. I tried something like this but I dont't want a picker to open.
This is my code so far. It is just downloading the file and converts it to a bytearray. The next step should by saving the file and opening it.
lifecycleScope.launch {
withContext(Dispatchers.IO) {
try {
Snackbar.make(binding.root, getString(R.string.load_document_started), Snackbar.LENGTH_LONG).show()
val documentData = DocumentDao().get(document.id, montageOrder)
val docAsByte = Base64.decode(documentData.data, Base64.DEFAULT)
val currentDateString = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val fileName = document.documentType.
lowercase()
.replace("ä", "ae")
.replace("ü", "ue")
.replace("ö", "oe") +
"_" + currentDateString
val file = File(requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName)
val fileStream = FileOutputStream(file)
fileStream.write(docAsByte)
fileStream.close()
val target = Intent(Intent.ACTION_VIEW)
target.setDataAndType(Uri.fromFile(file), "application/pdf")
target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
val intent = Intent.createChooser(target, "Yolo");
startActivity(intent)
} catch (e: Exception) {
Log.e(TAG, "Dokument konnte nicht geladen werden: " + e.message, e)
Snackbar.make(binding.root, getString(R.string.exception_could_not_load_document), Snackbar.LENGTH_LONG).show()
}
}
}
This results in a FileUriExposedException
Another aproach was using the SAF
lateinit var docAsByte : ByteArray
private val createFileLauncher = registerForActivityResult(CreatePdfDocument()) { uri ->
lifecycleScope.launch {
withContext(Dispatchers.IO) {
val stream = requireContext().contentResolver.openOutputStream(uri)
stream?.write(docAsByte)
stream?.close()
val target = Intent(Intent.ACTION_VIEW)
target.setDataAndType(uri, "application/pdf")
target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
startActivity(target)
}
}
}
private fun setGui() {
_binding?.lvDocuments?.adapter = DocumentAdapter(requireContext(), montageOrder.documents)
_binding?.lvDocuments?.setOnItemClickListener { parent, _, position, _ ->
val document : Document = parent.getItemAtPosition(position) as Document
lifecycleScope.launch {
withContext(Dispatchers.IO) {
try {
val currentDateString = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val fileName = document.documentType.
lowercase()
.replace("ä", "ae")
.replace("ü", "ue")
.replace("ö", "oe") +
"_" +
montageOrder.orderNumber +
"_" +
currentDateString +
".pdf"
Snackbar.make(binding.root, getString(R.string.load_document_started), Snackbar.LENGTH_LONG).show()
val documentData = DocumentDao().get(document.id, montageOrder)
docAsByte = Base64.decode(documentData.data, Base64.DEFAULT)
createFileLauncher.launch(fileName)
} catch (e: Exception) {
Log.e(TAG, "Dokument konnte nicht geladen werden: " + e.message, e)
Snackbar.make(binding.root, getString(R.string.exception_could_not_load_document), Snackbar.LENGTH_LONG).show()
}
}
}
}
}
Everything works fine except for opening. But if I open the pdf via file explorer it works.
Found a thread online and solved it this way: https://www.py4u.net/discuss/614761
Add provider_paths.xml to xml resource folder
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
In your manifest add a FileProvider:
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
Prepare to download files to any directory your app owns, such as getFilesDir(), getExternalFilesDir(), getCacheDir() or getExternalCacheDir().
val privateDir = context.getFilesDir()
Download file taking its progress into account (DIY):
val downloadedFile = myFancyMethodToDownloadToAnyDir(url, privateDir, fileName)
Copy it to Downloads folder:
private val DOWNLOAD_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val finalUri : Uri? = copyFileToDownloads(context, downloadedFile)
fun copyFileToDownloads(context: Context, downloadedFile: File): Uri? {
val resolver = context.contentResolver
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, getName(downloadedFile))
put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(downloadedFile))
put(MediaStore.MediaColumns.SIZE, getFileSize(downloadedFile))
}
resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
} else {
val authority = "${context.packageName}.provider"
val destinyFile = File(DOWNLOAD_DIR, getName(downloadedFile))
FileProvider.getUriForFile(context, authority, destinyFile)
}?.also { downloadedUri ->
resolver.openOutputStream(downloadedUri).use { outputStream ->
val brr = ByteArray(1024)
var len: Int
val bufferedInputStream = BufferedInputStream(FileInputStream(downloadedFile.absoluteFile))
while ((bufferedInputStream.read(brr, 0, brr.size).also { len = it }) != -1) {
outputStream?.write(brr, 0, len)
}
outputStream?.flush()
bufferedInputStream.close()
}
}
Once in download folder you can open file from app like this:
val authority = "${context.packageName}.provider"
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(finalUri, getMimeTypeForUri(finalUri))
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
try {
context.startActivity(Intent.createChooser(intent, chooseAppToOpenWith))
} catch (e: Exception) {
Toast.makeText(context, "Error opening file", Toast.LENGTH_LONG).show()
}
//Kitkat or above
fun getMimeTypeForUri(context: Context, finalUri: Uri) : String =
DocumentFile.fromSingleUri(context, finalUri)?.type ?: "application/octet-stream"
//Just in case this is for Android 4.3 or below
fun getMimeTypeForFile(finalFile: File) : String =
DocumentFile.fromFile(it)?.type ?: "application/octet-stream"
Through base64 string iam creating a pdf file.
This pdf file is succesfully created and stored in data/users/0/cache
On a button click i want to show this pdf file.
Function in kotlin:
fun decodeArbeidscontractString() {
var fos: FileOutputStream? = null
try {
f = File(context?.cacheDir, "newPdf_f.pdf")
f!!.createNewFile()
fos = FileOutputStream(f)
val decodedString: ByteArray = Base64.decode(pdf_string_arbeidscontract,
Base64.DEFAULT)
fos.write(decodedString) //write the base64 to the pdf
fos.flush()
fos.close()
val path: Uri = Uri.fromFile(f)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(path, "application/pdf")
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
}
}
I can see the file in the emulator but cannot open it. How can i do that