I am trying to allow the user to share an image to other apps on the device. The image is inside the files/ subdirectory of my app's internal storage area. It works just fine with Gmail, but Facebook and Twitter both crash when responding to my intent.
EDIT: Google+ also works fine.
Here are the relevant sections of code.
In Application.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.iforce2d.myapp.MyActivity"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
xml/filepaths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="shared" path="shared"/>
</paths>
Here is the sharing code in my activity:
File imagePath = new File(getContext().getFilesDir(), "shared");
File newFile = new File(imagePath, "snapshot.jpg");
Uri contentUri = FileProvider.getUriForFile(getContext(),
"org.iforce2d.myapp.MyActivity", newFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
List<ResolveInfo> resInfos =
getPackageManager().queryIntentActivities(shareIntent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : resInfos) {
getContext().grantUriPermission(info.activityInfo.packageName,
contentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivity(Intent.createChooser(shareIntent, "Share image..."));
The value of contentUri
when logged is:
content://org.iforce2d.myapp.MyActivity/shared/snapshot.jpg
I have only checked this with Gmail, Facebook and Twitter as the receiving apps, but the results have been very consistent over a wide range of OS versions (from 2.2.1 to 4.4.3) and 7 devices include a Kindle.
Gmail works great. Image thumbnail appears in mail composition, and successfully attaches to mail when sent.
Twitter and Facebook both crash, as below.
Here are the stack traces from logcat showing the problem these two apps are having, it appears to be the same problem for both of them (this is taken from 4.4.3, but the errors were practically the same all the way back to 2.2.1 albeit with slightly different wording of the error message):
Caused by:
java.lang.IllegalStateException: Couldn't read row 0, col 0 from CursorWindow.
Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:434)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
at android.database.CursorWrapper.getString(CursorWrapper.java:114)
at com.twitter.library.media.util.f.a(Twttr:95)
...
Caused by:
java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.
Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:434)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
at android.database.CursorWrapper.getString(CursorWrapper.java:114)
at com.facebook.photos.base.media.MediaItemFactory.b(MediaItemFactory.java:233)
...
Given that sharing images on Facebook and Twitter is something that millions of people do all day long, I'm pretty shocked that it's so hard to implement :/
Can anybody spot something I'm doing wrong here?
Twitter (wrongly) assumes that there will be a MediaStore.MediaColumns.DATA column. Starting in KitKat the MediaStore returns null, so luckily, Twitter gracefully handles nulls, and does the right thing.
public class FileProvider extends android.support.v4.content.FileProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor source = super.query(uri, projection, selection, selectionArgs, sortOrder);
String[] columnNames = source.getColumnNames();
String[] newColumnNames = columnNamesWithData(columnNames);
MatrixCursor cursor = new MatrixCursor(newColumnNames, source.getCount());
source.moveToPosition(-1);
while (source.moveToNext()) {
MatrixCursor.RowBuilder row = cursor.newRow();
for (int i = 0; i < columnNames.length; i++) {
row.add(source.getString(i));
}
}
return cursor;
}
private String[] columnNamesWithData(String[] columnNames) {
for (String columnName : columnNames)
if (MediaStore.MediaColumns.DATA.equals(columnName))
return columnNames;
String[] newColumnNames = Arrays.copyOf(columnNames, columnNames.length + 1);
newColumnNames[columnNames.length] = MediaStore.MediaColumns.DATA;
return newColumnNames;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With