I have an app that has image upload feature and after uploading the image on onActivityResult() I receive the Uri of the image and turn it into bitmap with the following function :
private fun uriToBitmap(selectedFileUri: Uri): Bitmap? {
return try {
val parcelFileDescriptor: ParcelFileDescriptor =
requireContext().contentResolver.openFileDescriptor(selectedFileUri, "r")!!
val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
val image = BitmapFactory.decodeFileDescriptor(fileDescriptor)
parcelFileDescriptor.close()
image
} catch (e: IOException) {
e.printStackTrace()
null
}
}
but for some reason my image rotates 90 degrees if it was a portrait image
I have tried using the ExifInterface thing for fixing it and rotating it back with this function :
fun determineImageRotation(imageFile: File, bitmap: Bitmap): Bitmap {
val exif = ExifInterface(imageFile.absolutePath)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)
val matrix = Matrix()
when (orientation) {
6 -> matrix.postRotate(90f)
3 -> matrix.postRotate(180f)
8 -> matrix.postRotate(270f)
}
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
but I receive this error :
ExifInterface got an unsupported image format file(ExifInterface supports JPEG and some RAW image formats only) or a corrupted JPEG file to ExifInterface.
java.io.EOFException
this is how I create a file path for the image :
#Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String =
SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"PNG_${timeStamp}_", /* prefix */
".png", /* suffix */
storageDir /* directory */
)
}
here is an example of a file path that I have for an image :
/storage/emulated/0/Android/data/avedot.app/files/Pictures/PNG_20210622_094219_232594250744276112.png
I assume there is something with the path that is related to the dot in avedot.app whice breaks the ExifInterface function but how can I work around this ?
Thanks in advance !
Related
I currently have a huge amount of PNGs in my assets folder. When i launch my app i retrieve the file path of each one and use it to convert to a bitmap when that specific png is needed like so
fun getImageBitmapFromAsset(assetManager: AssetManager, imagePath: String?): ImageBitmap {
val inputStream: InputStream
var bitmap: Bitmap
try {
inputStream = assetManager.open(imagePath!!)
bitmap = BitmapFactory.decodeStream(inputStream)
} catch (e: IOException) {
return emptyImageBitmap
}
return bitmap.asImageBitmap()
}
My question is can this be done with SVGs and how?
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()
}
}
I am trying to save a picture taken from the camera and making it appear in the device's gallery.
It's working well for Android Q, but for P the images are not showing up.
I tried to follow the Documentation as closely as I could
Manifest has:
android.permission.WRITE_EXTERNAL_STORAGE
I'm not getting any crashes or catches, I just don't know why it is not showing up in the gallery, can anyone please point me in the right direction to fix this? Much appreciated.
This method is called from the onActivityResult() when taking a picture from the camera.
private fun saveImageFromCamera(
activity: Activity,
imageView: ImageButton,
photoFile2: File?,
uriPath: (String?) -> Unit
) {
try {
// If on Android 10+ save to Pictures folder and private folder
val savedUri = Uri.fromFile(photoFile2)
val bitmap = getBitmap(activity, savedUri)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (bitmap != null) {
try {
saveImage(activity, savedUri, photoFile2!!.name)
} catch (e: Exception) {
e.printStackTrace()
}
}
} else {
if (bitmap != null) {
try {
if (PermissionsHelper.checkExternalStorageAccess(activity)) {
val newFile = createImageFile(activity)
addBitmapToFile(newFile, bitmap)
galleryAddPic(activity)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}
imageView.load(savedUri)
} catch (e: Exception) {
Toast.makeText(activity, "Failed to load", Toast.LENGTH_SHORT).show()
}
}
Function that writes a bitmap into destination file
fun addBitmapToFile(destinationFile: File, bitmap: Bitmap) {
val bos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos)
val bitmapData = bos.toByteArray()
//write the bytes in file
val fos = FileOutputStream(destinationFile)
fos.write(bitmapData)
fos.flush()
fos.close()
}
Creating a file to store the image in, using getExternalFilesDir.
#Throws(IOException::class)
fun createImageFile(activity: Activity): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat(FILENAME, Locale.US).format(Date())
val storageDir: File = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
return File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
).apply { currentPhotoPath = absolutePath }
}
I do get an error when creating a file at this line:
return File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
).apply { currentPhotoPath = absolutePath }
2020-07-23 12:01:40.906 14705-14705/app.app W/System.err: java.io.IOException: Not a directory 2020-07-23
12:01:40.906 14705-14705/app.app W/System.err: at
java.io.UnixFileSystem.createFileExclusively0(Native Method)
2020-07-23 12:01:40.906 14705-14705/app.app W/System.err:
at
java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:280)
2020-07-23 12:01:40.906 14705-14705/app.app W/System.err:
at java.io.File.createNewFile(File.java:948) 2020-07-23 12:01:40.906
14705-14705/app.app W/System.err: at
java.io.File.createTempFile(File.java:1862) 2020-07-23 12:01:40.906
14705-14705/app.app W/System.err: at
app.app.helpers.CameraAndGalleryHelper$Companion.createImageFile(CameraAndGalleryHelper.kt:338)
Getting the bitmap from a Uri, I am able to populate an ImageView from this bitmap, so I know that's not the issue.
fun getBitmap(context: Context, imageUri: Uri): Bitmap? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ImageDecoder.decodeBitmap(
ImageDecoder.createSource(
context.contentResolver, imageUri
)
)
} else {
context.contentResolver.openInputStream(imageUri)?.use { inputStream ->
BitmapFactory.decodeStream(inputStream)
}
}
}
Here is the scanner so that the image shows up in the gallery
private fun galleryAddPic(activity: Activity) {
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent ->
val f = File(currentPhotoPath)
mediaScanIntent.data = Uri.fromFile(f)
activity.sendBroadcast(mediaScanIntent)
}
}
When using the code above, I can take pictures on devices Android 8 and up without any problems.
But when using a Samsung S6 on Android 7, the images do not show up in the device's gallery.
Android:requestLegacyExternalStorage="true" for AndroidManifest.xml
How about trying to describe? However, this option will also be removed in the next android12.
I've found lots of answers related tothis in Java, supposedely a Kotlin solution would be very much like Java, but as in many other things, the devil is on the details, and there are some.
I have several Uris stores in the SQLite database I'm using, now I want to send this images to an API that will catch them along with other data. I'll send the info via POST.
So now, I want to load the Uri, as I do when I use an ImageView.setImageURI() that will take the Uri, convert to Bitmap and place it in the ImageView container.
How do I convert from that Uri to a Bitmap object, and then encode it to Base64 to send it to the API, using Kotlin code?
EDIT
I'm trying with Anupam's imageFileToBase64() which seems to be exactly what I want, now I'm having a problem, I got a FileNotFoundException. This is what I'm doing.
I recover the Uri string from the database, it is a string that reads: content://media/external/images/media/109, so I convert it to an Uri
val uri = Uri.parse(uri_string)
Then I get the real path, and convert it to File
val file = File(uri.path)
Finally I call the function
val base64 = imageFileToBase64(file)
I have tried both with uri.path and uri.toString() and got the same results.
uri.path = /external/images/media/109
uri.toString() = content:/media/external/images/media/109
So I got no idea on what to pass to the function.
These are Kotlin methods for the following -
1. Get the bitmap from assets
2. Save bitmap to a file
3. Get Base64 from bitmap
4. Encode File/Image to Base64
5. Decode Base64 to File/Image
// Get the bitmap from assets and display into image view
val bitmap = assetsToBitmap("tulip.jpg")
// If bitmap is not null
bitmap?.let {
image_view_bitmap.setImageBitmap(bitmap)
}
val imagePath = "C:\\base64\\image.png"
// Encode File/Image to Base64
val base64ImageString = encoder(imagePath)
println("Base64ImageString = $base64ImageString")
// Decoder Base4 to File/Image
decoder(base64ImageString, "C:\\base64\\decoderImage.png")
// Click listener for button widget
button.setOnClickListener{
if(bitmap!=null){
// Save the bitmap to a file and display it into image view
val uri = bitmapToFile(bitmap)
image_view_file.setImageURI(uri)
// Show a toast message
toast("Bitmap saved in a file.")
}else{
toast("bitmap not found.")
}
}
}
// Method to get a bitmap from assets
private fun assetsToBitmap(fileName:String):Bitmap?{
return try{
val stream = assets.open(fileName)
BitmapFactory.decodeStream(stream)
}catch (e:IOException){
e.printStackTrace()
null
}
}
// Method to save an bitmap to a file
private fun bitmapToFile(bitmap:Bitmap): Uri {
// Get the context wrapper
val wrapper = ContextWrapper(applicationContext)
// Initialize a new file instance to save bitmap object
var file = wrapper.getDir("Images",Context.MODE_PRIVATE)
file = File(file,"${UUID.randomUUID()}.jpg")
try{
// Compress the bitmap and save in jpg format
val stream:OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG,100,stream)
stream.flush()
stream.close()
}catch (e:IOException){
e.printStackTrace()
}
// Return the saved bitmap uri
return Uri.parse(file.absolutePath)
}
// Method to get Base64 from bitmap
private fun imageFileToBase64(imageFile: File): String {
return FileInputStream(imageFile).use { inputStream ->
ByteArrayOutputStream().use { outputStream ->
Base64OutputStream(outputStream, Base64.DEFAULT).use { base64FilterStream ->
inputStream.copyTo(base64FilterStream)
base64FilterStream.flush()
outputStream.toString()
}
}
}
}
// Encode File/Image to Base64
private fun encoder(filePath: String): String{
val bytes = File(filePath).readBytes()
val base64 = Base64.getEncoder().encodeToString(bytes)
return base64
}
// Decode Base64 to File/Image
private fun decoder(base64Str: String, pathFile: String): Unit{
val imageByteArray = Base64.getDecoder().decode(base64Str)
File(pathFile).writeBytes(imageByteArray)
}
I'm trying to create an application where i am required to add or delete an image simultaneously from image view and external storage. While doing the same, when I try adding the new image into the imageview using Uri, the old image keeps getting added again.
Here is my code
if (resultCode == Activity.RESULT_OK && requestCode == IMAGE_PICK_CODE_GALLERY) {
var selectedImage = data?.data
try {
val bitmap = MediaStore.Images.Media.getBitmap(context?.contentResolver,selectedImage)
if(bitmap!=null) {
val imageURI: String = getImageUri(context!!, bitmap)
}
private fun getImageUri(context: Context, inImage: Bitmap): String {
var fOut: OutputStream?
var path: String? = null
var fileName: String? = abc
var file: File? = null
file = File(
Environment.getExternalStorageDirectory().toString() + File.separator + "myDirectory",
"$fileName"
)
if (file.exists())
{
file.getCanonicalFile().delete()
if (file.exists())
{
context?.deleteFile(file.getName())
}
file.delete()
}
file.createNewFile() //If file already exists will do nothing
fOut = FileOutputStream(file)
inImage.compress(Bitmap.CompressFormat.JPEG, 40, fOut)
Glide.with(this).load(file).into(imageView!!)
fOut.flush()
fOut.close()
// path = MediaStore.Images.Media.insertImage(context.contentResolver,file.absolutePath,file.getName(),null);
} catch (ex: Exception) {
ex.printStackTrace()
}
return file.toString()
}
Glide caches your images, so probably you are loading a cached version of the old image.
As suggested in Glide's docs you should add a signature to handle cache invalidation:
Glide.with(yourFragment)
.load(yourFileDataModel)
.signature(new ObjectKey(yourVersionMetadata))
.into(yourImageView);