I'm using FileProvider API for sharing content actually storing in internal storage.
Following is my xml configuration that linked with Provider configured in Manifiest file.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="xs_audio" path="/audio/records"/>
</paths>
and code that I'm using to share is following:
private final static String FILE_PROVIDER = BuildConfig.APPLICATION_ID + ".fileprovider";
private String testPackageAppHaveAccess = "com.whats";
public static void shareDocument(Activity activity, CallRecordData data) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putExtra(Intent.EXTRA_SUBJECT, "Record File");
intent.setType("audio/*");
ArrayList<Uri> files = new ArrayList<>();
//for (AudioModelObj image : data.getDocuments()) {
files.add(getImageUri(activity, data.getFile()));
//}
activity.grantUriPermission(testPackageAppHaveAccess, files.get(0), Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files);
activity.startActivity(Intent.createChooser(intent, "Share Audio File."));
}
private static Uri getImageUri(Activity activity, String audioURI) {
if (Build.VERSION.SDK_INT < 24) {
return Uri.parse(audioURI);
} else {
URI uri = URI.create(audioURI);
File file = new File(uri);
return FileProvider.getUriForFile(activity, FILE_PROVIDER, file);
}
}
}
but while launching with app it's not attaching anything. In case of gmail it say "can't attach empty file". File is confirmedly available as I'm displaying list of file and playing.
For reference: Uri generating from getImageUri(..) is
/data/user/0/com.xl.cl.debug/cache/audio/records/17-10-17_170728_abc_.wav
Any suggestion what I'm doing wrong ?
<files-path> already points to what on some devices will be /data/user/0/com.xl.cl.debug
<cache-path> is what you should be using, replacing path with just the subdirectory of interest (audio/records), eliminating the /data/user/0/com.xl.cl.debug/cache bit
You are calling grantUriPermission(), where the first parameter is not a package
You are calling grantUriPermission(), where the first parameter is not a package identifying the app to which you are trying to grant permission
You are not adding FLAG_GRANT_READ_URI_PERMISSION to the Intent, which is the typical way of saying "the Uri in this Intent should be readable by the recipient of this Intent" (though it is possible that ACTION_SEND_MULTIPLE requires more work here, as I haven't played with that much)
No filesystem path in the human history has begun with content:/, so get rid of that
Calling new File() and supplying a value that is not a filesystem path is not going to work well
There may be more problems than those, but that should get you started
Related
I'm building an app that allows the user to save the bitmap or share it without saving it. The 2nd functionality doesn't quite work. I understand that the app needs to save the file to the device before sharing it on a social media app so my idea was, immediately after the file was successfully shared, to automatically delete the file from the device. I've build a delete method trying 2 different approaches and neither have worked:
First approach:
public void deleteFile(String path){
File file = new File(path);
try {
file.getCanonicalFile().delete();
} catch (IOException e) {
e.printStackTrace();
}
}
Second approach:
public void deleteFile(String path){
File file = new File(path);
boolean deleted = file.delete();
}
And I'm calling deleteFile(String) from the sharing method:
public void shareMeme(Bitmap bitmap) {
String path = MediaStore.Images.Media.insertImage(Objects.requireNonNull(getContext()).getContentResolver(), bitmap, "Meme", null);
Uri uri = Uri.parse(path);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, "This is my Meme");
getContext().startActivity(Intent.createChooser(share, "Share Your Meme!"));
deleteFile(path);
}
With respect to your stated problem, insertImage() returns a string representation of a Uri. That Uri is not a file. Calling getPath() on it is pointless, and you cannot delete anything based on that path.
More broadly, if your intention is to delete the content right away:
Do not put it in the MediaStore
Do not share it, as you will be deleting it before the other app has a chance to do anything with it
If you want to share it, but then delete it:
Do not put it in the MediaStore
Delete it the next day, or in a few hours, or something, as you have no good way of knowing when the other app is done with the content
To share an image with another app without using the MediaStore:
Save the image to a file in getCacheDir() (call that on a Context, such as an Activity or Service)
Use FileProvider to make that file available to other apps
Beyond that:
Do not use wildcard MIME types in ACTION_SEND. You are the one who is supplying the content to send. You know the actual MIME type. Use it.
Note that there is no requirement for an ACTION_SEND activity to honor both EXTRA_TEXT and EXTRA_STREAM. Most seem to do so, but that behavior is outside of the ACTION_SEND specification.
Note that insertImage() is deprecated on Android Q.
First, you need to check if your file exists, (maybe you set the wrong path?). Then delete the file
File file = new File(path);
if (file.exists()){
if (file.delete()) {
Toast.makeText(this, "file Deleted :" + path, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "file not Deleted :" + path, Toast.LENGTH_SHORT).show();
}
}
My app is providing the functionality to share files between users. For image files there is a custom gallery, and by long clicking, it opens images in local Image Viewer via ACTION_VIEW intent. Here is the code:
public static boolean openFile(Context context, File file)
{
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String extension = FilenameUtils.getExtension(file.getPath());
String mimeType = mimeTypeMap.getMimeTypeFromExtension(extension.toLowerCase());
Intent openFile = new Intent(Intent.ACTION_VIEW);
openFile.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|Intent.FLAG_ACTIVITY_NO_HISTORY
|Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri fileUri;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
String authority = context.getApplicationContext().getPackageName() + ".provider";
try {
fileUri = FileProvider.getUriForFile(context, authority, file);
} catch (IllegalArgumentException e) {
if (Fabric.isInitialized()) Crashlytics.logException(e);
else e.printStackTrace();
return false;
}
}
else fileUri = Uri.fromFile(file);
openFile.setDataAndType(fileUri, mimeType != null ? mimeType : "*/*");
if (openFile.resolveActivity(context.getPackageManager()) != null)
{
context.startActivity(openFile);
return true;
}
return false;
}
All works fine on API < 24, but on 24+ I've faced an issue trying to open image on sdcard. The exception is:
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/89F3-1B06/Pictures/DSC_0004.JPG
W/System.err: at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:719)
W/System.err: at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:404)
Apparently, this is due to the fact that <external-path name="other" path="."/> doesn't provide access on the level of sdcard in the file tree. So I've added this path:
<external-path name="external_files" path="../../"/> . That helped, but something tells me, that it's not the best solution.
I've found another solution, which works too:
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
fileUri = Uri.fromFile(file);
But disabling StrictMode restrictions looks even worse. So it's not on option.
Any suggestion on how to solve this issue the right way?
FileProvider cannot serve files from removable SD card.
Only from getFilesDir(), getExternalFilesDir() and getExternalStorageDirectory().
You should make your own file provider if you wanna serve files from sd card.
I am trying to share image file in cache directory, i have the complete path, but not able to send the file in attachments, the code is
File shareImage=Utils.getBitmapFile();
Log.d("Activity", "get final path in result"+shareImage.getAbsolutePath());
/*MimeTypeMap mime = MimeTypeMap.getSingleton();
String ext=shareImage.getName().substring(shareImage.getName().lastIndexOf(".")+1);
String type = mime.getMimeTypeFromExtension(ext);
shareIntent.setType(type);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
Uri shareImageUri = Uri.fromFile(shareImage);
Uri shareImageUri = Uri.fromParts("content", shareImage.getAbsolutePath(), null);//("content://"+shareImage.getAbsolutePath());
*/
Uri shareImageUri = Uri.fromFile(shareImage);
Log.d("Result ","uri is "+shareImageUri.toString());
shareIntent.putExtra(Intent.EXTRA_STREAM, shareImageUri);
startActivity(Intent.createChooser(shareIntent, "Share Results"));
the above commented code is not working
the send mail shows attachment,but not receiving end there is not attachment,
facebook sharing also shows no image in post
what the reason for this??
I have already seen the following SO Links how-to-use-share-image-using-sharing-intent-to-share-images-in-android and many others, none of them are able to resolve the issue
P.S.;
1.The aim is to take screenshot of screen save it in cache directory and share it online from there
2.Yes i do have file, I can pull it via DDMS from device and see on system.
I followed #CommonsWare's advice and used a FileProvider. Assuming your image is already in the internal cache directory as cache/images/image.png, then you can use the following steps. These are mostly a consolidation of the documentation.
Set up the FileProvider in the Manifest
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
...
</application>
</manifest>
Replace com.example.myapp with your app package name.
Create res/xml/filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="shared_images" path="images/"/>
</paths>
This tells the FileProvider where to get the files to share (using the cache directory in this case).
Share the image
File imagePath = new File(context.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = FileProvider.getUriForFile(context, "com.example.app.fileprovider", newFile);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
shareIntent.setDataAndType(contentUri, getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
Documentation
FileProvider
Storage Options - Internal Storage
Sharing Files
Saving Files
what the reason for this?
As noted, other apps do not have access to your app's internal storage.
none of them are able to resolve the issue
Feel free to open a fresh StackOverflow question, where you explain, completely and precisely what specific solutions you have tried and what specific problems you have encountered.
but that does not seems to be working as per SO post!!!
Feel free to open a fresh StackOverflow question, where you explain, completely and precisely what "that does not seems to be working" means.
Or, use FileProvider, which offers this capability with no code required beyond an entry for it in your manifest.
Or, store your image on external storage, such as getExternalCacheDir().
I share cached image by followed steps .
1.Copy your cached image to target path.
public static File copyImage(String sourcePath, String targetPath){
try {
InputStream in = new FileInputStream(sourcePath);
OutputStream out = new FileOutputStream(targetPath);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
return new File(targetPath);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
2.Get the Uri of copy file.
Uri uri = Uri.fromFile(target);
3.Share image by intent
File dir = new File(Constant.INKPIC_PATH);//your custom path,such as /mnt/sdcard/Pictures
if(!dir.exists()){
dir.mkdirs();
}
File f = new File(dir,"temporary_file.jpg");
File target = copyImage(url,f.getAbsolutePath());
Uri uri = Uri.fromFile(target);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM,uri );
context.startActivity(intent);
I am using the DownloadManager to download an image to the system's gallery and then in the Broadcast receiver (once the download succeeds) using an Intent to set the image as the wallpaper.
Everything was working fine but then recently on 4.4 I started to get an exception in the Photos/Google+ app because it is expecting a content URI and not a file URI.
So my question is if anyone knows how to convert a full file path/URI (file://) into a content style URI (content://)?
Sorry for the lack of source code, I am away from the computer that has the source, but I hope the question makes sense without it, get a content style uri from a full path.
EDIT:
The image is copied into the system's gallery or media gallery, not saved within my apps internal storeage.
Here is an example of what I want to convert:
file:///storage/emulated/0/Pictures/Rockstar/image.jpg
to
content://media/internal/images/media/445
EDIT 2:
Here is the error that I get from the Google+ app:
04-21 10:50:35.090: E/AndroidRuntime(7220): FATAL EXCEPTION: main
04-21 10:50:35.090: E/AndroidRuntime(7220): Process: com.google.android.apps.plus, PID: 7220
04-21 10:50:35.090: E/AndroidRuntime(7220): java.lang.RuntimeException: Unable to resume activity
{com.google.android.apps.plus/com.google.android.apps.photos.phone.SetWallpaperActivity}:
java.lang.IllegalArgumentException: Image URI must be of the content scheme type
Here is the code that I use to let the user set the wallpaper:
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
Uri u = Uri.parse(uriString);
Intent wall_intent = new Intent(Intent.ACTION_ATTACH_DATA);
wall_intent.setDataAndType(u, "image/*");
wall_intent.putExtra("mimeType", "image/*");
Intent chooserIntent = Intent.createChooser(wall_intent,
"Set As");
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(chooserIntent);
}
Where uriString is:
file:///storage/emulated/0/Pictures/Rockstar/image.jpg
I was able to figure it out. It was a combination of the code found here: Converting android image URI and scanning the media file after downloading.
So after the file finished downloading I get the path and do the following:
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
//Update the System
Uri u = Uri.parse(uriString);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, u));
//Get the abs path using a file, this is important
File wallpaper_file = new File(u.getPath());
Uri contentURI = getImageContentUri(context, wallpaper_file.getAbsolutePath());
For some reason starting the media scanner, newing the file, and getting the absolute path are important, I'm not exactly sure why but I can't spend any more time on this!
The way to convert from a file URI to a content URI is as follows (taken from the linked StackOver flow post:
public static Uri getImageContentUri(Context context, String absPath) {
Log.v(TAG, "getImageContentUri: " + absPath);
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
, new String[] { MediaStore.Images.Media._ID }
, MediaStore.Images.Media.DATA + "=? "
, new String[] { absPath }, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI , Integer.toString(id));
} else if (!absPath.isEmpty()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, absPath);
return context.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
Maybe this will help someone in the future.
So my question is if anyone knows how to convert a full file path/URI (file://) into a content style URI (content://)?
Implement a ContentProvider. FileProvider offers an out-of-the-box solution for serving up local files.
I'm not sure about the technique you are using to set the wallpaper but the easiest way is probably to use WallpaperManager.setStream() which doesn't require any URI.
Also note that a file URI only works between apps if the file is publicly accessible so a content URI is a more general solution.
Using a content URI implies that a ContentProvider will serve the file. Which one depends on where your file is located.
If your app has a direct read access to the file, you can implement a content provider in your app by using for example the FileProvider class of the support library, but this should really only be used if the file is located in the private data storage of your app.
If the image is added to the system media gallery, you should probably use the URI provided by the MediaStore.
(I have read a lot of similar questions, but bear with me here)
I need to send an image from one Activity (custom camera acitvity), where the second Activity is to upload the image to Picasa Web Album via Google API.
Every example I've found goes something like this:
File f = new File(cacheDir, "image_name.jpg");
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
startActivity(intent);
This works perfectly fine when I use the standard Android Picasa upload activity (or any other sharing app). I can also upload photos via the Picasa example app I am using, when sharing the image from gallery/camera etc.
But I cannot figure out how to build an Intent which uses a "content://---" uri and pass this to another application (neither for this example app or the Picasa standard app)...
Specificially: How can I create an Intent which is compatible with the code below (i.e. uses "content://" uri instead of "file://" uri)?
static class SendData {
String fileName;
Uri uri;
String contentType;
long contentLength;
SendData(Intent intent, ContentResolver contentResolver) {
Bundle extras = intent.getExtras();
if (extras.containsKey(Intent.EXTRA_STREAM)) {
Uri uri = this.uri = (Uri) extras.get(Intent.EXTRA_STREAM);
String scheme = uri.getScheme();
if (scheme.equals("content")) {
Cursor cursor = contentResolver.query(uri, null, null, null, null);
cursor.moveToFirst();
this.fileName = cursor.getString(cursor.getColumnIndexOrThrow(Images.Media.DISPLAY_NAME));
this.contentType = intent.getType();
this.contentLength = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE));
}
}
}
}
Retrieving the File-information from a File uri manually leads to NullPointerException with the Google Http Request used in the app.
Hardcoding the Content uri works. E.g:
uploadIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://media/external/images/media/11"));
Information related to Media files is stored by the MediaStore, which is a ContentProvider (http://developer.android.com/reference/android/provider/MediaStore.Images.html)
MediaStore.Images.DATA column corresponds to the file path and MediaStore.Images._ID column corresponds to the ID.
You need to query for the ID corresponding to your file path and then create a ContentUri out of it (which will be MediaStore.Images.Media.EXTERNAL_CONTENT_URI + id if the image is on the external storage, I'll try to think of a better way to translate the ID into a Content Uri).
http://developer.android.com/reference/android/provider/MediaStore.Images.Media.html#query(android.content.ContentResolver, android.net.Uri, java.lang.String[]