I am recording a video with the camerax library, I would like to retrieve the uri of the recorded file to open it with InputStream but it keeps giving me FILENOTFOUND
This is the code I use to start recording the video
contentValues = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, currentFile!!.name)
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Movies/" + DataHolder.getInstance().albumName)
put(MediaStore.MediaColumns.DATE_TAKEN, System.currentTimeMillis())
}
requireContext().contentResolver.run {
val mediaStoreOutput = MediaStoreOutputOptions.Builder(
requireActivity().contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
.setContentValues(contentValues)
.build()
currentRecording = videoCapture.output.prepareRecording(requireActivity(), mediaStoreOutput)
.apply { withAudioEnabled() }
.start(mainThreadExecutor, captureListener)
val resolver = requireContext().contentResolver
currentUri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)
And this is when I try to initialize InputStream:
try {
val resolver: ContentResolver = requireContext().getContentResolver()
val fis: InputStream? = resolver.openInputStream(currentUri!!)
}
catch (e: Exception) {
Log.e("asd", "Error File not found")
}
Try using this path instead:
val path = app.externalMediaDirs[0].absolutePath
val dir = File(path, "/videos")
if (!dir.exists()) {
val success = dir.mkdirs()
if (!success) {
return
// error/no file found
}
}
val file = File(dir , name)
It has become tricky in the new sdk versions to write to storage
In this way you are placing the video in a directory you can later fetch it from
Related
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"
I am try to take photo in private folder and save to public media store.
val takePictureContract = registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) {
uri?.let { fileUri ->
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "pic_${System.currentTimeMillis()}")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
}
val mediaUri = contentResolver.insert(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
contentValues
)
mediaUri?.let { mUri ->
contentResolver.openOutputStream(mUri)?.use { os ->
contentResolver.openInputStream(fileUri)?.use { inputStream ->
inputStream.copyTo(os)
}
}
}
}
}
}
fun takePic() {
val currentTimeMillis = System.currentTimeMillis()
val folder = File(filesDir, "images")
val file = File(folder, "pic_${currentTimeMillis}")
folder.mkdirs()
uri = FileProvider.getUriForFile(this, "${packageName}.provider", file)
takePictureContract.launch(uri)
}
But after take photo, I have this error message. What's wrong with my code?
Error message I get is :
java.lang.UnsupportedOperationException: Writing to internal storage is not supported
If change INTERNAL_CONTENT_URI to EXTERNAL_CONTENT_URI, and require permission WRITE_EXTERNAL_STORAGE, I can save photo success.
So, I need to save image to the Picture folder. There will be some subfolders too. But every time I want to save an image it is giving this error:
Failed to create new mediaStore record
This is what I tried:
#RequiresApi(Build.VERSION_CODES.Q)
#Throws(IOException::class)
fun saveMediaToStorage(context: Context, bitmap: Bitmap, format: CompressFormat, fName: String, folderName: String, extension: String) {
//Generating a file name
val filename = if (fName.contains("png")) fName.replace(".png", "") else fName.replace(".jpg", "")
//Output stream
var fos: OutputStream? = null
val relativePath = Environment.DIRECTORY_PICTURES + File.separator + folderName
Log.d(TAG, "saveMediaToStorage: Relative Path: $relativePath")
//getting the contentResolver
context.contentResolver?.also { resolver ->
//Content resolver will process the contentvalues
val contentValues = ContentValues().apply {
//putting file information in content values
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/$extension")
put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath)
}
//Inserting the contentValues to contentResolver and getting the Uri
val imageUri= resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) ?:
throw IOException("Failed to create new mediaStore record") // This line is triggering every time
//Opening an outputstream with the Uri that we got
fos = imageUri.let { resolver.openOutputStream(it) }
}
fos?.use {
//Finally writing the bitmap to the output stream that we opened
bitmap.compress(format, 100, it)
}
}
What I can understand is, the imageUri is coming null. But why so, afaik I am doing ok..
What can be the reason, where I am doing wrong.. Any help would be highly appreciated.
I'm using below code for downloading the image and it download successfully but not showing in galary please help!!!
I have tried couple of examples none of them are working..
I have also tried the MediaScanner to scan the file and update the gallery
If anyone have links related this question please share
class DownloadFileFromURL : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void): Void? {
var count: Int
try {
val url = URL(getUrl())
val conection: URLConnection = url.openConnection()
conection.connect()
// input stream to read file - with 8k buffer
val input: InputStream = BufferedInputStream(url.openStream(), 8192)
val output: OutputStream?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver: ContentResolver = activity?.contentResolver!!
val contentValues = ContentValues()
val name = System.currentTimeMillis()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "$name.jpg")
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
output = imageUri?.let { resolver.openOutputStream(it) }!!
} else {
val imgFile = createImageFile()
if (!imgFile.exists()) {
imgFile.createNewFile()
}
output = FileOutputStream(imgFile)
}
// Output stream to write file
val data = ByteArray(1024)
var total: Long = 0
while (input.read(data).also { count = it } != -1) {
total += count.toLong()
// writing data to file
output.write(data, 0, count)
}
// flushing output
output.flush()
// closing streams
output.close()
input.close()
} catch (e: Exception) {
Log.e("Error: ", e.message)
}
return null
}
override fun onPostExecute(result: Void?) {
downloadComplete()
}
#Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val storageDir: File = activity?.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
return File(
storageDir, /* directory */
"JPEG_${timeStamp}"+ /* prefix */
".jpg" /* suffix */
).apply {
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = absolutePath
}
}
}
In Android Studio I want to save a BitMap to a specific folder in the galery of the android device for example /test_pictures as an image.
The easy ways I found on the internet seem to be all deprecated, so it is not good practice to use those.
Does anyone have an easy example code on how to achieve this in Kotlin?
Kotlin bitmap extension like this:
fun Bitmap.saveImage(context: Context): Uri? {
if (android.os.Build.VERSION.SDK_INT >= 29) {
val values = ContentValues()
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test_pictures")
values.put(MediaStore.Images.Media.IS_PENDING, true)
values.put(MediaStore.Images.Media.DISPLAY_NAME, "img_${SystemClock.uptimeMillis()}")
val uri: Uri? =
context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
saveImageToStream(this, context.contentResolver.openOutputStream(uri))
values.put(MediaStore.Images.Media.IS_PENDING, false)
context.contentResolver.update(uri, values, null, null)
return uri
}
} else {
val directory =
File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES).toString() + separator + "test_pictures")
if (!directory.exists()) {
directory.mkdirs()
}
val fileName = "img_${SystemClock.uptimeMillis()}"+ ".jpeg"
val file = File(directory, fileName)
saveImageToStream(this, FileOutputStream(file))
if (file.absolutePath != null) {
val values = contentValues()
values.put(MediaStore.Images.Media.DATA, file.absolutePath)
// .DATA is deprecated in API 29
context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
return Uri.fromFile(file)
}
}
return null
}
fun saveImageToStream(bitmap: Bitmap, outputStream: OutputStream?) {
if (outputStream != null) {
try {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
outputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
val imageUri = YourBitmap.saveImage(applicationContext)