My android app flow is as follows:
Creates a csv file.
Store it in the default app folder.
Generates a notification of succesful creation
Open the csv in the default app.
I can not be able to open the csv via Intent or other methods so far.
With the following code I'm able to open the explorer but not in the folder path.
var path = this.applicationContext.getExternalFilesDir(null)?.absolutePath.toString()
println("Hey my path is $path")
val csvWriter = CSVWriter(
fileWriter,
CSVWriter.DEFAULT_SEPARATOR,
CSVWriter.NO_QUOTE_CHARACTER,
CSVWriter.DEFAULT_ESCAPE_CHARACTER,
CSVWriter.DEFAULT_LINE_END
)
csvWriter.writeAll(data.toMutableList())
csvWriter.close()
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/*"
}
startActivityForResult(intent, 2)
Also, I tried to develop a function but without effective response:
fun open_file(filename: String?) {
val path = File(filesDir, "dl")
val file = File(path, filename)
// Get URI and MIME type of file
val uri = FileProvider.getUriForFile(this, "$PACKAGE_NAME.fileprovider", file)
val mime = contentResolver.getType(uri)
// Open file with user selected app
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.setDataAndType(uri, mime)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
}
I saw some possible solutions developed in java but there are deprecated and / or does not work converted in Kotlin.
Edit:
Given the suggestion of #CommonsWare, I'm trying to save the file in the path given but the user. However, I get the error : No Activity found to handle Intent
val FILE_EXPORT_REQUEST_CODE = 12
val exportIntent = Intent(Intent.ACTION_CREATE_DOCUMENT)
exportIntent.addCategory(Intent.CATEGORY_OPENABLE)
exportIntent.type = "text/csv"
val uri = Uri.parse("$path/file.csv")
exportIntent.data = uri
val filename = "file.csv"
exportIntent.putExtra(Intent.EXTRA_TITLE, filename)
startActivityForResult(exportIntent, FILE_EXPORT_REQUEST_CODE)
Thanks in advance.
Related
This question already has answers here:
exposed beyond app through ClipData.Item.getUri
(4 answers)
Closed 8 months ago.
I want to share my application using a share button inside it. Once the button is clicked it should get the base.apk from the package manager and then share it using Intents.
Here is what I have so far:
All UI is ready and working
I have the following code to get the app and share it
try {
val pm = packageManager
val ai = pm.getApplicationInfo(packageName, 0)
val srcFile = File(ai.publicSourceDir)
val share = Intent()
share.action = Intent.ACTION_SEND
share.type = "application/vnd.android.package-archive"
share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(srcFile))
startActivity(Intent.createChooser(share, "Sharing"))
} catch (e: Exception) {
UtilityMethods(this).toast("Failed To Share The App", "e")
e.printStackTrace()
}
But I get an error with this procedure.
android.os.FileUriExposedException: file:///data/app/~~BC-clKZDViP_O7n44ooPbQ%3D%3D/MyAppPublicSourceDirectory/base.apk exposed beyond app through ClipData.Item.getUri()
Is there any help I can get regarding this? I tried a lot of solutions, but they don't work for me.
EDIT:: Updated Code, Copy the base.apk to Downloads Folder and Rename it. Then try to share it (which is where the error invokes from).
try {
// get the base apk of the app
val pm = packageManager
val ai = pm.getApplicationInfo(packageName, 0)
val srcFile = File(ai.publicSourceDir)
// save the file in Downloads folder
val dstFile = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"LogsCalculator.apk"
)
dstFile.createNewFile()
val input = FileInputStream(srcFile)
val output = FileOutputStream(dstFile)
val buffer = ByteArray(1024)
var length: Int = input.read(buffer)
while (length > 0) {
output.write(buffer, 0, length)
length = input.read(buffer)
}
output.flush()
output.close()
input.close()
// share the apk file now
val intent = Intent(Intent.ACTION_SEND)
intent.type = "application/vnd.android.package-archive"
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(dstFile))
startActivity(Intent.createChooser(intent, getString(R.string.sharing)))
} catch (e: Exception) {
UtilityMethods(this).toast("Failed To Share The App", "e")
e.printStackTrace()
}
It Still Does Not Work.
On later API versions, not even backup tools can obtain the APK anymore, therefore the approach is literally pointless. Instead use Firebase Dynamic Links, in order to permit user to user sharing of your application. This way they can install from Google Play Store, instead up installing some APK of unknown origin, which may not update well.
I got the solution.
try {
// get the base.apk
val baseApkLocation =
applicationContext.packageManager.getApplicationInfo(
applicationContext.packageName,
PackageManager.GET_META_DATA
).sourceDir
// get the file
val baseApk = File(baseApkLocation)
// the path
val path = Environment.getExternalStorageDirectory().toString() + "/Download/"
// make the directory
val dir = File(path)
// if the directory doesn't exist, make it
if (!dir.exists()) {
dir.mkdirs()
}
// Copy the .apk file to downloads directory
val destination = File(
path + "MyAppName.apk"
)
if (destination.exists()) {
destination.delete()
}
destination.createNewFile()
val input = FileInputStream(baseApk)
val output = FileOutputStream(destination)
val buffer = ByteArray(1024)
var length: Int = input.read(buffer)
while (length > 0) {
output.write(buffer, 0, length)
length = input.read(buffer)
}
output.flush()
output.close()
input.close()
// get content uri for the file
val uri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID + ".provider",
destination
)
// share the file
val intent = Intent(Intent.ACTION_SEND)
intent.type = "application/vnd.android.package-archive"
intent.putExtra(Intent.EXTRA_STREAM, uri)
startActivity(Intent.createChooser(intent, getString(R.string.share_app)))
} catch (e: Exception) {
Lib(this).toast("Failed To Share The App", "e")
e.printStackTrace()
}
Here is how it works
Get the base.apk file of the app from its source dir.
Copy the file to the new location and give it a meaningful name.
Get the content URI of the new file. This is what was missing.
// get content uri for the file
val uri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID + ".provider",
destination
)
Share the file.
Anyways thanks for all the help.
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)
I am currently working with one of my app updates and I am looking for a way to save some files at the root of shared internal storage.
Don't confuse with the word shared here. I just meant here with the phone's internal/external storage which holds a large amount of data.
Now coming to the main point, I have an app that uses the FFmpeg library for android and it records the live streams and saves it into the phone's storage.
Now here's the problem I don't want to save this file in my app package folder.
e.g: /storage/emulated/0/Android/data/com.app.package/files/...
I want to save this video file in the root of the storage folder where I can create a special folder for my app just like WhatsApp then save all required data in it only.
e.g: /storage/emulated/0/MyApp/...
To be more concise I want my app just like WhatsApp which has a separate folder in internal storage for storing its app-related data.
Now, so far I tried these.
getExternalFilesDir(null)?.absolutePath -> /storage/emulated/0/Android/data/com.app.package/files
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath -> /storage/emulated/0/Android/data/com.app.package/files/Download
externalCacheDir?.absolutePath -> /storage/emulated/0/Android/data/com.app.package/cache
filesDir?.absolutePath -> /data/user/0/com.app.package/files
Environment.getRootDirectory().absolutePath -> /system
Environment.getExternalStorageDirectory().absolutePath -> /storage/emulated/0
Environment.getExternalStoragePublicDirectory("").absolutePath -> /storage/emulated/0
Now these to methods can work for me but they are deprecated getExternalStorageDirectory() & getExternalStoragePublicDirectory()
Just keep in mind my solution is related to the WhatsApp storage folder.
I searched a lot but not so much help as I don't want to go with just a copy-paste and hack solutions. I want to do it in a clean way.
I am trying to open a File Picker via intent for this I have set permission
....
....
and for intent.
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "*/*"
intent.flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
startActivityForResult(Intent.createChooser(intent, "Select .... file"), RC)
Now here on Android 10, a file picker is not opening and this works perfectly if I add the requestLegacyExternalStorage flag in the manifest.
Or
if I set android:maxSdkVersion="29" also worked but here google wants to use Some SAF or Media Store API but I don't get it I just want to pick a file just a simple txt nothing else.
You can save video file to external storage using this:
#RequiresApi(api = Build.VERSION_CODES.Q)
#Throws(FileNotFoundException::class)
fun addToApiAbove29Gallery(
context: Context,
file: File?,
fileName: String?,
destinationPath: String?,
action: () -> Unit
) {
val valuesvideos: ContentValues
valuesvideos = ContentValues()
valuesvideos.put(
MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM
+ File.separator + context.resources.getString(R.string.app_name)
+ File.separator + File(destinationPath).name
)
valuesvideos.put(MediaStore.Video.Media.TITLE, "${fileName}.mp4")
valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, "${fileName}.mp4")
valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis())
valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 1)
val resolver = context.contentResolver
val collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val uriSavedVideo = resolver.insert(collection, valuesvideos)
val pfd = context.contentResolver.openFileDescriptor(uriSavedVideo!!, "w")
val executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
val handler = Handler(Looper.getMainLooper())
executor.execute {
if (pfd != null) {
try {
val out =
FileOutputStream(pfd.fileDescriptor)
val inputStream = FileInputStream(file)
val buf = ByteArray(8192)
var len: Int
while (inputStream.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
out.close()
inputStream.close()
pfd.close()
valuesvideos.clear()
valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 0)
try {
setExifFromUri(
context,
uriSavedVideo,
File(destinationPath)
)
} catch (e: IOException) {
e.printStackTrace()
}
} catch (e: Exception) {
e.printStackTrace()
}
handler.post {
context.contentResolver.update(uriSavedVideo, valuesvideos, null, null)
action()
}
}
}
}
Note that this code is only for android 10 and above, because you can easily save the video file to ecternal storage below android 10.
I store the pdf file using this code:
val intent = Intent().apply {
action = Intent.ACTION_CREATE_DOCUMENT
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
putExtra(Intent.EXTRA_TITLE, file.name)
}
startActivityForResult(intent, SAVE_FILE_REQUEST_CODE)
and after at onActivityResult Im saving data using OutputStream. Uri to file can look like:
content://com.android.providers.downloads.documents/document/4594
or
content://com.google.android.apps.docs.storage/document/documentId
But when im trying to start activity with this intent, pdf viewer app show error:
val openFileIntent = Intent(Intent.ACTION_VIEW, contentUri)
.apply {
setDataAndType(contentUri, "application/pdf")
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}
Is it possible to open pdf viewer activity from content Uri?
I can confirm that the Comments have the answer: blackapps and Андрей Макаренко
val target = Intent(Intent.ACTION_VIEW)
target.setDataAndType(uri, "application/pdf")
target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_GRANT_READ_URI_PERMISSION
val intent: Intent = Intent.createChooser(target, "Open File")
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
// Instruct the user to install a PDF reader here
}
Adding the "or Intent.FLAG_GRANT_READ_URI_PERMISSION" to the flags got my PDF reader to open the file correctly with the content:// Uri structure.
I'm trying to open the CSV file downloaded using DownloadManager using the apps available in the phone. But I am not able to open it. The third party apps returns giving error Toast messages or Alert Dialog saying file location not found. But when, I open file manually, the app is getting opened normally.
I have written below code for accessing the downloaded file and opening using Intent Chooser.
fun openDownloadedFile(referenceId : Long){
if (downloadReference === referenceId)
{
val query = DownloadManager.Query()
query.setFilterById(downloadReference)
var downloadManager = activity?.getSystemService(DOWNLOAD_SERVICE) as DownloadManager?
val c = downloadManager?.query(query)!!
if (c.moveToFirst())
{
val columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS)
if (DownloadManager.STATUS_SUCCESSFUL === c.getInt(columnIndex))
{
var localUri:String = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
if (localUri.substring(0, 7).equals("file://")) {
localUri = localUri.substring(7);
}
var file = File(localUri);
val fileExtension = MimeTypeMap.getFileExtensionFromUrl(localUri)
var mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension)
if (mimeType != null)
{
mimeType = "text/csv" // even after removing manual mime type it is not working.
var intent = Intent(Intent.ACTION_VIEW);
//var file = File(localUri);
var absoluteFilePath = file.getAbsolutePath();
var uri = Uri.parse( "content:/"+absoluteFilePath);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.setDataAndTypeAndNormalize(uri, mimeType);
try
{
// Add chooser to open file.
var intentChooser = Intent.createChooser(intent, "Choose Application");
startActivityForResult(intentChooser,1000);
}
catch (e:ActivityNotFoundException) {
}
}
} else {
Log.d(“TAG”, "Download Failed.. status : "+c.getInt(columnIndex))
}
}
}
}
When I chose, Microsoft Excel, it shows Cant open file error.
Any help is appreciated.