Hey devs I want to export my app entire view into PDF file and open it in pdf viewer, I explore so many things on google but my problem is not solved what i do so far is make bitmap of entire view but not figure out how I export it into PDF file, help regarding this would be appreciated
code i done so far is below:
val date = Date()
val format = DateFormat.format("MM-dd-yyyy_hh:mm:ss", date)
try {
val mainDir = File(
requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), "FilShare"
)
if (!mainDir.exists()) {
val mkdir = mainDir.mkdir()
}
path = "$mainDir/$u_id-$format.jpeg"
view?.isDrawingCacheEnabled = true
val bitmap = view?.let { Bitmap.createBitmap(it.drawingCache) }
view?.isDrawingCacheEnabled = false
val imageFile = File(path)
val fileOutputStream = FileOutputStream(imageFile)
bitmap?.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
fileOutputStream.flush()
fileOutputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
Related
I save a png image to external storage using this block of code for sdk<=28
/**
* save image with this method if the sdk is 28 or lower
*/
private fun saveImageSdk28(fileName: String){
//declar the output stream variable outside of try/catch so that it can always be closed
var imageOutputStream: FileOutputStream? = null
var outputImageFile = getFile(fileName)
if (!outputImageFile.exists()) {
outputImageFile.createNewFile()
}
try {
imageOutputStream = FileOutputStream(outputImageFile)
encryptedBitmap.compress(Bitmap.CompressFormat.PNG, 100, imageOutputStream)
} catch (e: IOException) {
e.printStackTrace()
Timber.i(e.toString())
} finally {
if (imageOutputStream != null) {
imageOutputStream.flush()
imageOutputStream.close()
}
}
}
/**
* returns file from fileName
*/
fun getFile(fileName: String): File{
//open, or create the directory where the image will be stored
var directory = File(
Environment.getExternalStorageDirectory().toString() + "/AppNameOutput/"
)
if (!directory.exists()) {
directory.mkdir()
}
//create the file
var file: File = File(directory.absolutePath, fileName)
return file
}
and this code for when the sdk>28
/**
* save image with this method if the sdk is 29 or higher
*/
#RequiresApi(Build.VERSION_CODES.Q)
private fun saveImageSdk29(fileName: String){
val imageCollection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "$fileName")
put(MediaStore.Images.Media.MIME_TYPE, "image/png")
put(MediaStore.Images.Media.WIDTH, encryptedBitmap.width)
put(MediaStore.Images.Media.HEIGHT, encryptedBitmap.height)
}
try{
val contentResolver = getApplication<Application>().contentResolver
contentResolver.insert(imageCollection, contentValues)?.also {uri->
contentResolver.openOutputStream(uri).use {outputStream ->
encryptedBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
}
}
}catch (e: IOException){
e.printStackTrace()
}
}
The image sucsessfully saves on the users device and can be accesed through files, however, the user can't access these images through the gallery, or Images tab.
I solved it. Turns out you just need to wait a while and reboot the phone for the gallery to show your images.
I am facing issue with Bitmap image quality when getting a image file from internal storage and show on imageView. How to show image file with original quality using bitmap.
Here is my code
fun renderPdf(renderer: PdfRenderer, pagesBitmap: MutableList<Bitmap>, dir: File) {
try {
for (i in 0 until PAGE_COUNT) {
val document: PDDocument = PDDocument.load(dir)
val pdfRenderer = PDFRenderer(document)
val bim = pdfRenderer.renderImage(i, 3.5f, Bitmap.Config.ARGB_8888!!)
pagesBitmap.add(bim!!)
document.close()
// Save the render result to an image
val path: String =
Environment.getExternalStorageDirectory().toString() + "/.AOF/aof_$i.jpg"
val renderFile = File(path)
val fileOut = FileOutputStream(renderFile)
pagesBitmap[i].compress(Bitmap.CompressFormat.JPEG, 100, fileOut)
fileOut.close()
}
ivForm.pagesBitmap = pagesBitmapFiles
ivForm.displayPDFDocument()
renderer.close()
} catch (e: IOException) {
Log.e("PdfBox-Android-Sample", "Exception thrown while rendering file", e)
} finally {
callDocumentType()
}
}
my App collecting some data from the user including an optional picture. To getting a High-Res picture i'm using following code:
https://medium.com/codex/how-to-use-the-android-activity-result-api-for-selecting-and-taking-images-5dbcc3e6324b
Getting the picture works as expected. If the user click on a save button, all data shall be written to an CSV-File on the SD-Card, and if latestTmpUri not null the user made a picture as well, and should be saved to the SD-Card, also.
I tried some snippets to move a file on Android, but everytime i'll get an error "File not exists". Maybe it has to do with path in provider_paths.xml, but i'm not sure.
By the way, i'm newbee on programming in Kotlin for Android.
EDIT:
If you take a look in the code from the URL above, there is an deleteOnExit()
private fun getTmpFileUri(): Uri {
val tmpFile = File.createTempFile("tmp_image_file", ".png", cacheDir).apply {
createNewFile()
deleteOnExit()
}
return FileProvider.getUriForFile(applicationContext, "${BuildConfig.APPLICATION_ID}.provider", tmpFile)
}
And if you look in provider_paths.xml
<cache-path name="cached_files" path="." />
<files-path name="images" path="." />
This is the path of the picture
content://com.company.contacts.provider/cached_files/tmp_image_file580022157706292749.png
To give an other path in <cache-path name="cached_files" path="." /> is not the solution i guess, because the SD-CARD's got a unique identifier, like E534-12F6
After a bit of research and thinking about FileInputStream and FileOutputStream and reading this post
https://stackoverflow.com/a/11327789/10155771
i got my solution. Depending on the Code in my first post to take a High-Res picture i modified it in this way:
private lateinit var tmpFile: File
private fun getTmpFileUri(): Uri {
tmpFile = File.createTempFile("tmp_image_file", ".png", cacheDir).apply {
createNewFile()
deleteOnExit()
}
return FileProvider.getUriForFile(applicationContext, "${BuildConfig.APPLICATION_ID}.provider", tmpFile)
}
to make the variable tmpFile global.
In my function to save the CSV and the optional picture i did this:
var imageName = ""
if(latestTmpUri != null) { // There was taken a picture if not null
val folder = getExternalFilesDirs(Environment.DIRECTORY_PICTURES)
val root = java.lang.String.valueOf(folder[1]).toString() // folder[1] is my SD-Card while folder[0] is internal storage
val filets: String = java.lang.String.valueOf(
TimeUnit.MILLISECONDS.toSeconds(
System.currentTimeMillis()
)
) // Unix Timestamp
imageName = companyContact +"_$filets.png"
var instream: InputStream? = null
var outstream: OutputStream? = null
try {
val dir: File = File(root, imageName.replace(" ", "_"))
instream = FileInputStream(tmpFile.path)
outstream = FileOutputStream(dir.path)
val buffer = ByteArray(1024)
var read: Int
while (instream!!.read(buffer).also { read = it } != -1) {
outstream!!.write(buffer, 0, read)
}
instream.close()
instream = null
outstream!!.flush()
outstream.close()
outstream = null
} catch (fnfe1: FileNotFoundException) {
fnfe1.message?.let { it1 -> Log.e("FileNotFoundException", it1) }
} catch (e: java.lang.Exception) {
Log.e("Exception", e.message!!)
}
}
Now i have my picture as png on my SD-Card.
Here is a simple solution for Kotlin 1.7.X
// move Uri file from cache to app's external files
// - from: data/data/com.example.myapp/cache/videos/file.mp4
// - to: sdcard/Android/data/com.example.myapp/files/Movies
// Note: not selectable by the user.
fun moveUriFileToAppMovies(fromUri: Uri, context: Context) {
val inputStream = context.contentResolver.openInputStream(fromUri)!!
val file = File(context.getExternalFilesDir(Environment.DIRECTORY_MOVIES),
fromUri.lastPathSegment ?: "video.mp4")
val outputStream = FileOutputStream(file)
inputStream.copyTo(outputStream)
inputStream.close()
outputStream.close()
}
I am currently writing an app where I need to create a zip file that has a bunch of bitmap images in it. I have a List that has the Uri's for all the images.
Could someone please direct me to how I can create a new zip file and then add all the images to a newly created zip file?
Assuming you have external storage permissions granted following should work
val BUFFER = 1024
fun Context.zip(files: Array<Uri>, zipFileName: String?) {
try {
var origin: BufferedInputStream? = null
val dest = FileOutputStream(zipFileName)
val out = ZipOutputStream(BufferedOutputStream(dest))
val data = ByteArray(BUFFER)
for (uri in files) {
val stringUri = uri.toString()
val fi = openFileInput(stringUri)
origin = BufferedInputStream(fi, BUFFER)
val entry = ZipEntry(stringUri.substring(stringUri.lastIndexOf("/") + 1))
out.putNextEntry(entry)
var count: Int
while (origin.read(data, 0, BUFFER).also { count = it } != -1) {
out.write(data, 0, count)
}
origin.close()
}
out.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
Remember this is an extension function on Context so it will require to be called with a context like context.zip(listOfUris, "ZIP_FILE_NAME_HERE")
I'm using Storage Access Network to pick file and save in internal storage so that app can use if in future.
I'm getting URI without any issues. It's something like content://com.android.providers.media.documents/document/image%3A141274
Problem comes when I'm trying to save image into internal directory. Code passes without crashes, image with same size is saved into internal directory (I can see it in device Explorer: https://take.ms/3TwBS).
But image itself is broken and can't be opened.
Here's code I'm using (after getting URI)
val destinationFile = File("${context.filesDir.absolutePath}/$fileName")
try {
val writer = FileWriter(destinationFile)
writer.append(readTextFromUri(it))
writer.flush()
writer.close()
} catch (e: Exception) {
e.printStackTrace()
}
#Throws(IOException::class)
private fun readTextFromUri(uri: Uri): String {
val inputStream = activity!!.contentResolver.openInputStream(uri)
val reader = BufferedReader(InputStreamReader(inputStream))
val stringBuilder = StringBuilder()
var line: String? = null
while ({ line = reader.readLine(); line }() != null) {
stringBuilder.append(line)
}
inputStream?.close()
reader.close()
return stringBuilder.toString()
}
As #CommonsWare described I should have used proper dealing with files, not texts.
Proper way to do:
private fun inputStreamToFile(uri: Uri){
val inputStream = contentResolver.openInputStream(uri)
val output = FileOutputStream(File("${filesDir.absoluteFile}/magic.png"))
inputStream?.copyTo(output, 4 * 1024)
}
Or longer way (without extension functions)
fun inputStreamToFile(uri: Uri){
val inputStream = contentResolver.openInputStream(uri)
inputStream.use {
val directory = getDir("test", Context.MODE_PRIVATE)
val file = File(directory, "correct.txt")
val output = FileOutputStream(file)
output.use {
val buffer = ByteArray(4 * 1024) // or other buffer size
var read: Int = inputStream?.read(buffer) ?: -1
while (read != -1) {
output.write(buffer, 0, read)
read = inputStream?.read(buffer) ?: -1
}
output.flush()
}
}
}