我之前的问题(Is it possible to share an image on Android via a data URL?)与这个问题有关。我已经想出了如何在没有权限将文件写入外部存储的情况下将图像从我的应用程序共享到另一个应用程序。但是,我仍然会遇到一些问题行为:
- 当我尝试从我的手机(Android 2.2.2)共享图像时,接收应用程序中出现致命错误,并且它们根本没有提供图像。(这可能是由于我的应用程序中的某些操作在 Android 2.2.2 上不受支持?或者这会导致我的应用程序而不是目标应用程序出现错误?)
- 当我尝试将图像共享到 Evernote 时,一切正常,但有时在保存笔记几秒钟后,我在应用程序屏幕底部(来自 Evernote 应用程序)收到一条消息:“java.lang.SecurityException:权限拒绝:从 ProcessRecord{413db6d0 1872:com.evernote/u0a10105}(pid=1872,uid=10105)打开提供程序 com.enigmadream.picturecode.PictureContentProvider 不是从 uid 10104 导出的“
- 当我尝试将图片分享到 Facebook 时,图片有一个矩形,但里面没有图片。
下面是我的 ContentProvider 代码。必须有一种更简单和/或更合适的方式来实现基于文件的 ContentProvider(尤其是查询功能)。我预计很多问题都来自查询实现。有趣的是,这在我的 Nexus 7 上运行 GMail 时效果很好。它也会为附件选择正确的显示名称和大小。
public class PictureContentProvider extends ContentProvider implements AutoAnimate {
public static final Uri CONTENT_URI = Uri.parse("content://com.enigmadream.picturecode.snapshot/picture.png");
private static String[] mimeTypes = {"image/png"};
private Uri generatedUri;
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new RuntimeException("PictureContentProvider.delete not supported");
}
@Override
public String getType(Uri uri) {
return "image/png";
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new RuntimeException("PictureContentProvider.insert not supported");
}
@Override
public boolean onCreate() {
generatedUri = Uri.EMPTY;
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
long fileSize = 0;
MatrixCursor result = new MatrixCursor(projection);
File tempFile;
try {
tempFile = generatePictureFile(uri);
fileSize = tempFile.length();
} catch (FileNotFoundException ex) {
return result;
}
Object[] row = new Object[projection.length];
for (int i = 0; i < projection.length; i++) {
if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.DISPLAY_NAME) == 0) {
row[i] = getContext().getString(R.string.snapshot_displaystring);
} else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.SIZE) == 0) {
row[i] = fileSize;
} else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.DATA) == 0) {
row[i] = tempFile;
} else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.MIME_TYPE)==0) {
row[i] = "image/png";
}
}
result.addRow(row);
return result;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
throw new RuntimeException("PictureContentProvider.update not supported");
}
@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
return mimeTypes;
}
private File generatePictureFile(Uri uri) throws FileNotFoundException {
if (generatedUri.compareTo(uri)==0)
return new File(getContext().getFilesDir(), "picture.png");;
Context context = getContext();
String query = uri.getQuery();
String[] queryParts = query.split("&");
String pictureCode = "016OA";
int resolution = 36;
int frame = 0;
int padding = 0;
for (String param : queryParts) {
if (param.length() < 2)
continue;
if (param.substring(0,2).compareToIgnoreCase("p=") == 0) {
pictureCode = param.substring(2);
} else if (param.substring(0,2).compareToIgnoreCase("r=") == 0) {
resolution = Integer.parseInt(param.substring(2));
} else if (param.substring(0, 2).compareToIgnoreCase("f=") == 0) {
frame = Integer.parseInt(param.substring(2));
} else if (param.substring(0, 2).compareToIgnoreCase("a=") == 0) {
padding = Integer.parseInt(param.substring(2));
}
}
Bitmap picture = RenderPictureCode(pictureCode, resolution, frame, padding);
File tempFile = new File(context.getFilesDir(), "picture.png");
FileOutputStream stream;
stream = new FileOutputStream(tempFile);
picture.compress(CompressFormat.PNG, 90, stream);
try {
stream.flush();
stream.close();
} catch (IOException e) {
e.printStackTrace();
throw new Error(e);
}
picture.recycle();
generatedUri = uri;
return tempFile;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File tempFile = generatePictureFile(uri);
return ParcelFileDescriptor.open(tempFile, ParcelFileDescriptor.MODE_READ_ONLY);
}
...
}
我在 AndroidManifest.xml 文件中也有这个作为<activity>
元素的兄弟:
<provider
android:name="PictureContentProvider"
android:authorities="com.enigmadream.picturecode.snapshot"
android:grantUriPermissions="true"
android:readPermission="com.enigmadream.picturecode.snapshot"
tools:ignore="ExportedContentProvider">
<grant-uri-permission android:path="/picture.png" />
</provider>
创建意图的代码如下所示:
resolution = mPicView.getWidth();
if (mPicView.getHeight() > resolution)
resolution = mPicView.getHeight();
String paddingText = mPadding.getEditableText().toString();
int padding;
try {
padding = Integer.parseInt(paddingText);
} catch (NumberFormatException ex) {
padding = 0;
}
Uri uri = Uri.parse(PictureContentProvider.CONTENT_URI
+ "?p=" + Uri.encode(mPicView.getPictureCode()) + "&r=" + Integer.toString(resolution)
+ "&f=" + Integer.toString(mPicView.getFrame()) + "&a=" + Integer.toString(padding));
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/png");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_subject_made));
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(share, getString(R.id.menu_share)));
编辑 当我的手机发生错误时,这是堆栈跟踪的前两行:
04-07 13:56:24.423: E/DatabaseUtils(19431): java.lang.SecurityException: Permission Denial: reading com.enigmadream.picturecode.PictureContentProvider uri content://com.enigmadream.picturecode.snapshot/picture.png? p=01v131&r=36&f=0&a=0 from pid=19025, uid=10062 需要 com.enigmadream.picturecode.snapshot
04-07 13:56:24.423: E/DatabaseUtils(19431): 在 android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:271)