I want to send multiple images to my database using retrofit. I am using this code to select multiple images.
private val galleryLauncher =
registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { list ->
//TODO convert all content uris to File
}
I have tried a bunch of image picker libraries but none of them works in my device (Android R).
How do I convert them to file? Please help.
Is there any other method to send images to server via Retrofit2?
I used this dependency
implementation "commons-io:commons-io:2.7"
And this method
private fun createFileFromUri(name: String, uri: Uri): File? {
return try {
val stream = context.contentResolver.openInputStream(uri)
val file =
File.createTempFile(
"${name}_${System.currentTimeMillis()}",
".png",
context.cacheDir
)
FileUtils.copyInputStreamToFile(stream, file) // Use this one import org.apache.commons.io.FileUtils
file
} catch (e: Exception) {
e.printStackTrace()
null
}
}
private val galleryLauncher =
registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { list ->
//TODO convert all content uris to File
}
Related
I am using the following function for making request and decoding URI from onActivity result . The following function works but freezes the whole screen for many seconds before generating the final file:
// Request code:
fun filePickerRequest3SAF(activity: AppCompatActivity) {
RequestIntentBuilder(IntentInit.OPEN_DOCUMENT) // Intent(Intent.ACTION_OPEN_DOCUMENT)
.addOpenableCategory()//requestIntent.addCategory(Intent.CATEGORY_OPENABLE)
.setFilteringMimeType("video/*")
.addFlagForReadPermission() //requestIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) and requestIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
.buildAndStartActivityForResult(activity)
}
//response function. here I pass received intent's URI and activity context
fun getPathFromUriOrEmpty(uri: Uri?, context: Context?): String {
if (context == null || uri == null) return ""
return getFileFromUriOrDefault(uri, context)?.path ?: ""
}
fun getFileFromUriOrDefault(uri: Uri?, context: Context?, default:File? = null): File? {
if (context == null || uri == null) return default
val resolver = context.contentResolver
val tmpFile = File(context.cacheDir, getFileNameFromUriOrNull(uri, resolver) ?: "temp_file_name.${getExtensionFromUriOrDefault(uri, context)}")
return try {
val inputStream = context.contentResolver.openInputStream(uri)
val outputStream = FileOutputStream(tmpFile)
outputStream.use { fileOut -> inputStream?.copyTo(fileOut) }
tmpFile
} catch (t: Throwable) {
t.printStackTrace()
default
}
}
is there a way to do it better, apart from just making a file and copying it as whole? My app is supposed to upload videos of size > 1-2 gb, so is there a way we can provide the URI / file to the upload service without actually making a copy of file? I am assuming the file upload service will also be making multiple copies to upload
Ps: I intent to support android versions KitKat to android 12/+ , so not thinking of using legacy storage or other such flags if they can be avoided with SAF as a unified solution across different android versions
Im trying to get mimetype of video files using below code.
Porblem: I have a file saved as audio but its mimetype is video/3gpp and it is also playing
as Audio. . How to detect this kind of file which is audio but mimetype is video/3gpp in its
details so i can exclude it from list.
fun getFileMimeType(url: String): String {
var type: String = ""
try {
if (url.lastIndexOf(".") != -1) {
val ext: String = url.substring(url.lastIndexOf(".") + 1)
val mime = MimeTypeMap.getSingleton()
type = mime.getMimeTypeFromExtension(ext)!!
}
} catch (e: Exception) {
}
return type
}
Assuming this is a local file, you can use MediaMetadataRetriever to get Metadata information about the media file.
Specifically, you can check if the METADATA_KEY_HAS_VIDEO is set to true.
An example in Kotlin:
val metadataRetriever = MediaMetadataRetriever()
metadataRetriever.setDataSource(context, Uri.fromFile(your3GPPFile))
val hasVideoKey = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO)
if (hasVideoKey == "yes") {
//Include in your list or do whatever work
//you need here
}
You can also use ExoPlayer and load the file, and then get a list of the tracks using
player.getCurrentTrackGroups()
This will return an array of tracks which you can loop through and parse the format to check if they include a video track.
I am using the third party library to open the Gallery and camera. I
have done that part. Now, when i select multiple image or single
image,got the array of URI from the third party lib. Now, i created the file
in app package folder and able to create it. But when i check under
app folder, the size of an image is 0 byte. I am saving the path also
on local db and later will upload it on server using multipart. Below
is my code.
To open the Gallery and camera
private fun openPicker() {
PhotoPickerFragment.newInstance(
multiple = true,
allowCamera = true,
maxSelection = 5,
theme = R.style.ChiliPhotoPicker_Light
).show(childFragmentManager, "picker")
}
got the selected image path URI and save path in to local db with createFile
override fun onImagesPicked(photos: ArrayList<Uri>) {
Log.e("TAG", "pic" + photos.joinToString(separator = "\n") { it.toString() })
fileList = ArrayList<File>()
try {
photos.forEachIndexed { index, e ->
println("$e at ${photos[index].path}")
val destinationFile: File = createImageFile()
fileList.add(destinationFile)
fileList.also {
// Get the file-name from the image-path
val destinationFilePath = it[index].absolutePath
val fileName =
destinationFilePath.substring(destinationFilePath.lastIndexOf("/") + 1)
val attachment = AttachSiteImage()
attachment.apply {
callLoggingId = callLoggingIdForAttachment
attachmentFileName = fileName
attachmentPath = destinationFilePath
}
attachImageviewModel?.addAttachFromApi(attachment)
}
}
Log.e("TAG", "Path->" + fileList.size)
} catch (ex: FileAlreadyExistsException) {
// sourceFile.delete()
cl_attachments_main_container.showSnackBar(
if (!ex.localizedMessage.isNullOrEmpty())
ex.localizedMessage
else
ex.stackTrace.toString(),
Snackbar.LENGTH_SHORT
)
} catch (ex: IOException) {
// sourceFile.delete()
cl_attachments_main_container.showSnackBar(
if (!ex.localizedMessage.isNullOrEmpty())
ex.localizedMessage
else
ex.stackTrace.toString(),
Snackbar.LENGTH_SHORT
)
}
}
Create the file where photos will be stored
#Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val storageDir: File? = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"${callLoggingIdForAttachment}_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
}
Here is the Library URL: https://github.com/ChiliLabs/ChiliPhotoPicker
Your createImageFile() function creates an empty file by returning a result of createTempFile, not an image file. You don't write anything to that empty file and use it as the attachment. And in
// Get the file-name from the image-path
val destinationFilePath = it[index].absolutePath
you don't use the image path as the comment says; you use the path of the empty file you just created.
The image file is given to you as Uri, e in
photos.forEachIndexed { index, e ->
but you ignore it except to log
println("$e at ${photos[index].path}")
So convert Uri to File
val destinationFile = File(e.path)
and work with that. Or copy it to the file created by createImageFile if that's what you want (answers there are for Java but IDEA/Android Studio can convert them to Kotlin for you).
For storing two-dimensional integer data (matrix) during the life of an application, it takes only one line of code (int myArray[][] = new int[][]) to store this data within the activity, and a relatively simple procedure (like for any other type of variable) using "intent" for transfer between activities.
However, if you want to store exactly the same data outside the runtime of the application, all the solutions that have been offered (for which I'm grateful) involve dozens of code lines.
Does anyone have a simple solution (I mean, as simple a the transfer between activities, or as simple as saving a string variable to SavedPreferences) for saving matrices outside application runtime?
You basically just need to convert the 2D array into a string and then store it in a file. Something along the following lines should work as you require.
private fun writeFileOnInternalStorage(context: Context, sFileName: String, sBody: Array<IntArray>) {
val file = File(context.getFilesDir(), "mydir")
if (!file.exists()) {
file.mkdir()
}
try {
val gpxfile = File(file, sFileName)
val writer = FileWriter(gpxfile)
writer.append(sBody.map {
//Convert the array to string using separators
it.joinToString(separator = ",")
}.joinToString(separator = "|"))
writer.flush()
writer.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun readFileOnInternalStorage(context: Context, sFileName: String): Array<IntArray> {
var result = emptyArray<IntArray>()
val file = File(context.getFilesDir(), "mydir")
if (!file.exists()) {
return result
}
try {
val gpxfile = File(file, sFileName)
val reader = FileReader(gpxfile)
val content = reader.readText()
//Convert the string back into a 2D array using the separators
result = content.split("|").map {
it.split(",").map {
Integer.parseInt(it)
}.toIntArray()
}.toTypedArray()
reader.close()
} catch (e: Exception) {
e.printStackTrace()
}
return result
}
I have written a c# web service that returns a pdf in a stream of bytes as response. Once I make a call to the web-service from my android app, I will store the response in an array byte till here I will be able to do it. But after that I need to convert that byte array into pdf, I should be able to display that. I have a menu page in which once the button is pressed the call is made to the web service with file name and on click of button I should be able to open pdf. Is this possible? Or there is some other, better solution? I checked on the net for better understanding, but I was unable to find one that could help me understand better.
Thanks for the suggestion, but I don't have the pdf in hand, I just have the array bytes, which I got from the web service. So I now need to regenerate the pdf from this array of bytes and display it, but I am not getting how to do it.
Try following these steps
Convert byte array to InputStream
val inputStream = ByteArrayInputStream(byteArray)
Save InputStream as PDF file:
suspend fun saveInputStreamAsPdfFile(inputStream: InputStream, applicationContext: Context): File? {
var outputFile: File? = null
withContext(Dispatchers.IO) {
try {
val directory = ContextCompat.getExternalFilesDirs(applicationContext, "documents").first()
val outputDir = File(directory, "outputPath")
outputFile = File(outputDir, UUID.randomUUID().toString() + ".pdf")
if (!outputDir.exists()) {
outputDir.mkdirs()
}
val outputStream = FileOutputStream(outputFile, false)
inputStream.use { fileOut -> fileOut.copyTo(outputStream) }
outputStream.close()
} catch (e: Exception) {
// Something went wrong
}
}
return outputFile
}
Show PDF with PdfRenderer
var totalPdfPages: Int
fun showPdf(pdfFile: File) {
val input = ParcelFileDescriptor.open(pdfFile, MODE_READ_ONLY)
val renderer = PdfRenderer(input)
val wrapper = PdfRendererWrapper(renderer)
totalPdfPages = wrapper.getTotalPages()
showPdfPage(0)
}
fun showPdfPage(currentPageIndex: Int) {
val pageBitmap = wrapper.getBitmap(currentPageIndex)
imageView.setImageBitmap(pageBitmap) // Show current page
}