如何实现ContentProvider的用于到Gmail,脸谱,Evernote的等提供图像(How

2019-08-20 04:38发布

我刚才的问题( 是否有可能通过数据URL分享Android上的图像? )是关系到这个问题。 我已想出如何分享我的应用程序到另一个应用程序的图像,而无需许可文件写入到外部存储设备。 不过,我仍然得到了一些问题行为:

  1. 当我试图从我的手机(安卓2.2.2)将影像分享的,发生在接收应用致命错误,他们不与图像上来的。 (难道这是我的应用程序的一些操作不支持在Android 2.2.2?还是那样引起我的应用程序,而不是目标应用程序错误的结果呢?)
  2. 当我尝试将图像分享到Evernote,一切工作正常,但笔记被保存有时几秒钟后,我在我的应用程序的屏幕的底部(从Evernote的应用程序)得到一个消息:“java.lang.SecurityException异常:许可拒绝:从ProcessRecord开口提供商com.enigmadream.picturecode.PictureContentProvider {413db6d0 1872:com.evernote / u0a10105}(PID = 1872,UID = 10105),其不从UID 10104" 输出
  3. 当我尝试分享照片至Facebook,还有一个供图片矩形,但它没有画面。

下面是我的ContentProvider代码。 必须有实现基于文件的ContentProvider(尤其是查询功能)的更容易和/或更合适的方式。 我想到了很多来自查询执行的问题。 有趣的是,这将Gmail时对我的Nexus 7非常漂亮的工作。 它拿起了附件正确的显示名称和大小了。

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异常:权限拒绝:读取com.enigmadream.picturecode.PictureContentProvider URI内容://com.enigmadream.picturecode.snapshot/picture.png? p =&01v131 R = 36&F = 0&从PID = 19025 a = 0时,UID = 10062要求com.enigmadream.picturecode.snapshot

04-07 13:56:24.423:E / DatabaseUtils(19431):在android.content.ContentProvider $ Transport.enforceReadPermission(ContentProvider.java:271)

Answer 1:

我不熟悉,我还是我不需要或如何禁用它们的权限

尝试更换:

<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>

有:

<provider 
    android:name="PictureContentProvider"
    android:authorities="com.enigmadream.picturecode.snapshot"
    android:exported="true"
    tools:ignore="ExportedContentProvider">
</provider>

你真的应该有android:exported="true"中的第一个也是如此,但权限更改我指的是代表去除android:readPermission<grant-uri-permissions >。

然后,甩掉addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 在Java代码中。

这将设置你ContentProvider成为世界可读。 从长期来看,这可能不是正确的答案。 短期来看,这将有助于确定您的Android 2.2.2的问题是,因为FLAG_GRANT_READ_URI_PERMISSION没有被兑现。



Answer 2:

我的问题与共享,以因为一些电子邮件和邮件客户端query方法。 有些收件人应用中发送nullprojection参数。 出现这种情况时,你的代码抛出一个NullPointerException 。 该NPE是很容易解决的。 然而,发送应用null仍然需要一些信息反馈。 我仍然无法分享到Facebook,但我可以分享到我已经使用测试的所有其他应用:

编辑我也不能让它与谷歌的Hangouts工作。 就这样,至少,我得到一个敬酒表明You can't send this file on Hangouts. Try using a picture You can't send this file on Hangouts. Try using a picture 。 也看到这个问题: 通过谷歌聚会照片删除了发后 。 我想这是因为我使用的是私有的内容和Hangouts不能/不会接受它的一些原因。

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
        String sortOrder) {
    if (projection == null) {
        projection = new String[] {
                MediaStore.MediaColumns.DISPLAY_NAME,
                MediaStore.MediaColumns.SIZE,
                MediaStore.MediaColumns._ID,
                MediaStore.MediaColumns.MIME_TYPE
        };
    }

    final long time = System.currentTimeMillis();
    MatrixCursor result = new MatrixCursor(projection);
    final File tempFile = generatePictureFile(uri);

    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] = uri.getLastPathSegment();
       } else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.SIZE) == 0) {
          row[i] = tempFile.length();
       } else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.DATA) == 0) {
          row[i] = tempFile;
       } else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.MIME_TYPE)==0) {
          row[i] = _mimeType;
       } else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.DATE_ADDED)==0 ||
               projection[i].compareToIgnoreCase(MediaStore.MediaColumns.DATE_MODIFIED)==0 ||
               projection[i].compareToIgnoreCase("datetaken")==0) {
           row[i] = time;
       } else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns._ID)==0) {
           row[i] = 0;
       } else if (projection[i].compareToIgnoreCase("orientation")==0) {
           row[i] = "vertical";
       }
    }

    result.addRow(row);
    return result;
}


Answer 3:

有同样的问题,并经过大量的研究和这里调试运行是我5分钱(对我的Android 2.3.3和4.2设备)

  1. Facebook应用程序将不使用openFile(Uri uri, String mode) ,所以我们有读取权限给它真正的URI(投影[I] == MediaStore.MediaColumns.DATA)。
  2. 你必须给每一个Facebook的它要求你的投影,否则FB-应用程序将只是repeate query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

这可能是一个黑客,并且不会对一些敏感数据的工作,但你可以file.setReadable(true, false); 在内部文件中的某个地方你的代码和Facebook将能够看到并发送IMG。 无需为Android外部存储的权限。 大概没有必要为内容提供商,如果IMG将是可读的。 可刚发的意图链接。 但我奇怪的谷歌+预览bechavior不提供因此决定保留它。 不要有很多设备的测试和使用Twitter的唯一,FB和Google+,Skype和Gmail中。 工作正常,现在

这里是GitHub上演示



文章来源: How to implement a ContentProvider for providing image to Gmail, Facebook, Evernote, etc