`Intent.ACTION_VIEW` fails with a `no file received` error - android

My current code :
Just focus on the fileOpener variable. The SafeFiles class is a self declared variable which has the file's extensions and such. Don't worry too much about it.
fileOpener = { file: File ->
val uri: Uri = Uri.fromFile(file).normalizeScheme()
val mime = MimeTypeMap
.getSingleton()
.getMimeTypeFromExtension(safeFile.extension.substring(1))
Log.d(TAG, "mime = $mime")
val intent = Intent(Intent.ACTION_VIEW)
intent.data = uri
intent.type = mime
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
Log.d(
TAG, "data received:" +
"uri = $uri\n" +
"extension = ${safeFile.extension.substring(1)}\n" +
"path = ${file.absolutePath}\n" +
"size = ${file.totalSpace} "
)
CoroutineScope(Dispatchers.Main).launch {
context!!.startActivity(intent)
}
}
Logs -
2022-07-30 16:41:41.005 19233-19285/com.example.cryptile D/SafeViewerFragment: mime = image/png
2022-07-30 16:41:41.005 19233-19285/com.example.cryptile D/SafeViewerFragment: data received:uri = file:///storage/emulated/0/Cryptile/CRYPTILE_2022_07_30/.CACHE/aa854c7e-9fa4-4313-9750-cf58fde467b8.png
extension = png
path = /storage/emulated/0/Cryptile/CRYPTILE_2022_07_30/.CACHE/aa854c7e-9fa4-4313-9750-cf58fde467b8.png
size = 119640424448
Clearly, the size not being zero means the file exists. I have also opened the mentioned file from the device's file explorer and everything seems to work fine. the only conclusion I came to is this might be some issue with the file provider. So, here's the manifest -
Manifest -
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cryptile">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<application
android:name=".app_data.AppApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.CRYPTILE">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.cryptile.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
</application>
</manifest>
And the filepaths.xml -
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="/storage/emulated/0"
path="." />
</paths>

the only conclusion I came to is this might be some issue with the file provider
You are not using FileProvider. You are using Uri.fromFile(), which has been all but banned for six years.
Replace:
val uri: Uri = Uri.fromFile(file).normalizeScheme()
with:
val uri: Uri = FileProvider.getUriForFile(context!!, "com.example.cryptile.fileProvider", file)
(and then rewrite your code to avoid the !!)

Adding this might have helped -
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
My final code is -
fileOpener = { file: File ->
val uri: Uri = FileProvider.getUriForFile(
requireContext(),
"com.example.cryptile.fileProvider",
file
)
val mime = MimeTypeMap
.getSingleton()
.getMimeTypeFromExtension(safeFile.extension.substring(1))
Log.d(TAG, "mime = $mime")
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.action = Intent.ACTION_VIEW
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
intent.setDataAndType(uri, mime)
Log.d(
TAG, "data received:" +
"uri = $uri\n" +
"extension = ${safeFile.extension.substring(1)}\n" +
"path = ${file.absolutePath}\n" +
"size = ${file.totalSpace} "
)
CoroutineScope(Dispatchers.Main).launch {
context!!.startActivity(intent)
}
}
Didn't make any changes anywhere else outside this lambda. Just make sure to compare the intent code because it is a little different compared to the question's code.

Related

How can I share pdf file in android?

I try to share a pdf file from android/data/mypackage/files/file.pdf
I generate these pdfs also in this app, and when I try to share it, the pdf doesn't appear in on attached files from email, or google drive says something like: "No data to share".
Here is my code for sharing pdf:
val aName = intent.getStringExtra("iName")
val file = File(this.getExternalFilesDir(null)?.absolutePath.toString(), "$aName")
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(Intent.EXTRA_STREAM, file)
shareIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
shareIntent.type = "application/pdf"
startActivity(Intent.createChooser(shareIntent, "share.."))
Toast.makeText(this,"$file",Toast.LENGTH_SHORT).show()
The pdf path looks correct when I toast it:
The problem is that you are not using a URI, just sending a path, you need several things.
Provider paths
You have to create provider_paths.xml under xml folder in res :
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path
name="files_root"
path="/" />
</paths>
Set the provider in the Manifest under Aplication:
<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>
Get the URI
fun uriFromFile(context:Context, file:File):Uri {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
}
else
{
return Uri.fromFile(file)
}
}
Your final code:
val aName = intent.getStringExtra("iName")
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(Intent.EXTRA_STREAM, uriFromFile(context,File(this.getExternalFilesDir(null)?.absolutePath.toString(), "$aName")))
shareIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
shareIntent.type = "application/pdf"
startActivity(Intent.createChooser(shareIntent, "share.."))
I didn't test the code, write it from "memory", let me know if it works for you.

FileProvider is unable to attach file to email client on Android 11

I used to attach a logs file, that I was storing in externalCacheDir, to a mail client.
Using this : intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(logFile))
However, starting from Android 11 it just stopped working. So I tried using FileProvider. But once the mail client opens it says "Couldn't attach file".
I have tried doing everything I could find here on SO and on the internet in general. But I can't seem to find the solution that will actually work.
Activity :
val logFile: File = File(globalContext.externalCacheDir, "MyLogFile.log")
fun Activity.openMail(
type: String = "text/plain"
): Boolean {
val intent = Intent(Intent.ACTION_SENDTO)
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("<some address...>"))
intent.putExtra(Intent.EXTRA_SUBJECT, "Android Logs")
intent.putExtra(Intent.EXTRA_TEXT, "some text")
val uri = FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.fileprovider", logFile)
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.type = type
intent.data = Uri.parse("mailto:")
if (intent.resolveActivity(packageManager) != null) {
startActivity(Intent.createChooser(intent, "Send email via::"))
return true
} else {
return false
}
}
Manifest :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
provider_paths :
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-cache-path name="external_cache" path="."/>
</paths>

Install apk FileProvider

I need to download .apk from server and promt user to install it. Its an actual update of b2b app and not malware.
For new android versions i need to use FileProvider.
Download works fine with download manager and on download is also triggered.
File is visible in device file explorer and File apk has correct path.
Allowed it in unknown apps.
Full app path: file:///storage/emulated/0/Android/data/com.example.trebovanje/files/Download/update_1.0-5.apk
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
<provider
android:name=".GenericFileProvider"
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>
And the code for install intent:
File apk = new File(Uri.parse(file).getPath());
if(apk.exists()) {
MimeTypeMap map = MimeTypeMap.getSingleton();
String ext = MimeTypeMap.getFileExtensionFromUrl(apk.getName());
String mimeType = map.getMimeTypeFromExtension(ext);
if (mimeType == null) mimeType = "*/*";
Intent promptInstall = new Intent(Intent.ACTION_VIEW);
promptInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
promptInstall.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
promptInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
promptInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //new androids
try {
Uri data = getFileUri(getApplicationContext(), apk);
promptInstall.setDataAndType(data, mimeType);
startActivity(promptInstall);
}catch(Exception e){
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
}else{ //old anrdoids
Uri data = Uri.fromFile(apk);
promptInstall.setDataAndType(data, mimeType);
startActivity(promptInstall);
}
}
private Uri getFileUri(Context context, File file) {
return FileProvider.getUriForFile(context, getPackageName() + ".provider", file);
}
I get always get "there is a problem parsing the package".

BroadcastReceiver and Open File after download

I have a project I'm working on that requires a file to be downloaded normally, and then opened (generally in a PDF reader).
I have a broadcast receiver class which looks like this
public class DownloadBroadcastReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
{
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0));
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Cursor cursor = manager.query(query);
if (cursor.moveToFirst()) {
if (cursor.getCount() > 0) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
Long download_id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,0);
String downloadFilePath = null;
String downloadFileLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
File file = new File(Uri.parse(downloadFileLocalUri).getPath());
// status contain Download Status
// download_id contain current download reference id
if (status == DownloadManager.STATUS_SUCCESSFUL) {
String fname=cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));
File pdfFile = new File(Environment.getExternalStorageDirectory()+"/Downloads/"+fname);//File path
if (pdfFile.isFile()) //Checking if the file exists or not
{
Uri path = Uri.fromFile(pdfFile);
Intent objIntent = new Intent(Intent.ACTION_VIEW);
objIntent.setDataAndType(path, "application/pdf");
objIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(objIntent);//Starting the pdf viewer
} else {
Log.d("OO",Environment.getExternalStorageDirectory()+"/Downloads/"+fname);
Log.d("OO",fname);
// Toast.makeText(getApplicationContext(),"Test",Toast.LENGTH_LONG).show();
}
}
}
}
cursor.close();
}
}
}
The Manifest looks like this
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uk.org.bridgewaterha.boardapp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".pdf"></activity>
<receiver android:name=".DownloadBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
</receiver>
<provider
android:name="android.support.v4.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>
</manifest>
the paths file is as below
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="Download" path="."/>
<cache-path name="cache" path="/" />
</paths>
The problem I'm having is that in the Broadcast receiver class, the line
if (pdfFile.isFile())
ALWAYS returns false.
I've tried a few variations I've come across on the web, but none seem to be working at all.
Any tips greatly appreciated.
After downloading you have to check your file is mFile.exists() or not...
use this to open pdf file:
I think your application default behavior is not opening it directly.
so we need to use chooser
Intent myIntent = new Intent(Intent.ACTION_VIEW);
myIntent.setData(Uri.fromFile(file));
Intent j = Intent.createChooser(myIntent, "Choose an application to open with:");
startActivity(j);
while (cursor.moveToNext()) {
if (cursor.getCount() > 0) {
Log.e("filelistingloop","loopfollowingss "+cursor.getCount());
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
String file = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (file != null) {
File mFile = new File(Uri.parse(file).getPath());
final String fileName = mFile.getAbsolutePath().substring(mFile.getAbsolutePath().lastIndexOf('/') + 1, mFile.getAbsolutePath().length());
if (mFile.exists()){
Toast.makeText(yourActivit.this," File donwloaded successfully",Toast.LENGTH_LONG).show();
//here you can open your pdf
}
}
// So something here on success
}else if (status == DownloadManager.STATUS_FAILED){
int message = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON));
// So something here on failed.
Toast.makeText(getActivity(),"File Not donwloaded "+ message,Toast.LENGTH_LONG).show();
}
}
}

how to share multiple files with ShareCompat

My question is how to share multiple files with ShareCompat intentBuilder in android.
My code throws exception:
android.content.ActivityNotFoundException: No Activity found to handle
Intent { act=android.intent.action.SEND_MULTIPLE flg=0x80001
pkg=com.google.android.gm (has clip) (has extras) }
My code is:
static void with(Activity activity, ArrayList<String> imageFile,String app){
ArrayList<Uri> imagesUri=new ArrayList<>();
for (String i:imageFile)
imagesUri.add(FileProvider.getUriForFile(
activity, FILES_AUTHORITY, new File(i)));
Intent shareIntent = ShareCompat.IntentBuilder.from(activity).getIntent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,imagesUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(shareIntent);
}
Is throws error for every app (package name).
the following works for me:
ShareCompat.IntentBuilder shareIntent = ShareCompat.IntentBuilder.from(activity).setType("image/*");
for(String address : addresses ) {
File file = new File(address);
Uri imageUri = FileProvider.getUriForFile(
activity,
"app.signature.fileprovider",
file);
shareIntent.addStream(imageUri);
}
Intent intent=shareIntent.getIntent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Choose Application"));
you should use addStream() to add multiple files.
After a very hard time searching on sites like stackoverflow. I found a solution in #Ian Lake's blog post that works for me and makes sense too.
ShareCompat.IntentBuilder shareIntent = ShareCompat.IntentBuilder.from((Activity) context)
.setType("video/image/*");
String[] files = parentDirectory.list();
if (files != null) {
for (String address : files) {
File file = new File(address);
File filePath = new File(context.getExternalFilesDir(null), "vids"); //vids is a directory local to my app in i.e storage/0/emulated/Android/data/com.myapp.videoeditor/vids
File newFile = new File(filePath, file.getName());
if (address.contains("cut_video")) {
Uri uri = FileProvider.getUriForFile(
context,
"com.myapp.videoeditor.fileprovider",
newFile);
shareIntent.addStream(uri);
}
}
}
Intent intent = shareIntent.getIntent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(intent, "Choose..."));
res/xml/file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="external_files" path="vids/"/>
</paths>
Manifest
<application
....
....>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.myapp.videoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<!-- ressource file to create -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
...
...
</application>
Did you add intent filter in AndroidManifest.xml for the activity that is supposed to receive the files?
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>

Categories

Resources