Совместное использование изображений работает для Gmail, но сбой FB и twitter
Я пытаюсь разрешить пользователю делиться изображением с другими приложениями на устройстве. Изображение находится внутри подкаталога files/моих внутренних областей хранения. Он отлично работает с Gmail, но Facebook и Twitter оба сбой при ответе на мои намерения.
EDIT: Google+ также отлично работает.
Вот соответствующие разделы кода.
В 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>
Вот код обмена в моей деятельности:
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..."));
Значение contentUri
при регистрации:
content://org.iforce2d.myapp.MyActivity/shared/snapshot.jpg
Я только проверил это с Gmail, Facebook и Twitter в качестве получающих приложений, но результаты были очень последовательными в широком диапазоне версий ОС (от 2.2.1 до 4.4.3), а 7 устройств - Kindle.
Gmail отлично работает. Изображение эскиза отображается в композиции почты и успешно прикрепляется к почте при отправке.
Twitter и Facebook оба сбой, как показано ниже.
Вот трассировка стека из logcat, показывающая проблему, с которой сталкиваются эти два приложения, похоже, для них одна и та же проблема (это взято из 4.4.3, но ошибки были практически одинаковы на всем пути назад до 2.2.1, хотя и с несколько иной формулировкой сообщения об ошибке):
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)
...
Учитывая, что обмен изображениями на Facebook и Twitter - это то, что миллионы людей делают в течение всего дня, я довольно шокирован тем, что его так сложно реализовать:/
Может ли кто-нибудь обнаружить то, что я делаю неправильно здесь?
Ответы
Ответ 1
EDIT - Обходной путь, описанный в этом ответе, работает, но решение, предоставляемое Stefan, более элегантно. Это должен быть принятый ответ.
Кроме того, с января 2015 года приложение Facebook больше не имеет этой ошибки (и теперь работает со стандартным FileProvider
), но Twitter все еще делает. Надеюсь, в будущем он полностью исчезнет, и ни один из них не понадобится.
Хотя объект FileProvider
выглядит замечательно в теории, он, похоже, не работает при совместном использовании со многими сторонними приложениями (в то время как Google, такие как G +, Gmail, и c работают отлично). Либо он не предоставляет достаточную информацию, либо эти приложения просто предполагают, что вы делите файлы, и, таким образом, сбой.
Это действительно расстраивает!:/
Единственный надежный способ обмена файлами изображений - скопировать их во внешний каталог хранилища (вы можете создать подкаталог .nomedia
, чтобы избежать их появления в приложении "Галерея" ).
Например:
Bitmap bitmapToShare = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
File pictureStorage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File noMedia = new File(pictureStorage, ".nomedia");
if (!noMedia.exists())
noMedia.mkdirs();
File file = new File(noMedia, "shared_image.png");
saveBitmapAsFile(bitmapToShare, file);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
shareIntent.setType("image/png");
startActivity(shareIntent);
Добавление: конечно, вы должны также проверить getExternalStorageState()
перед тем, как попытаться записать/скопировать туда файл.
Ответ 2
Twitter (ошибочно) предполагает, что будет столбец MediaStore.MediaColumns.DATA. Начиная с KitKat MediaStore возвращает null, так что, к счастью, Twitter изящно обрабатывает нули и делает правильные вещи.
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;
}
}