安卓分享意图选择器仅限邮件客户端

5
我正在尝试为用户创建一个选项,只能通过电子邮件从我的应用程序发送文件。该文件是应用程序内部的,并且可以通过FileProvider访问。
这是contentURI的样子:content://packagename.files/files/somefile.ext
正如您所看到的,我正在提供给用户将文件共享到PicsArt、Google Drive、OneDrive和EMail。

ShareDialogFragment

我可以成功地将内容分享给前三个客户,因为他们有非常具体的应用程序。但是当涉及到电子邮件时,我需要用户从他在手机上安装的应用程序中选择客户端。
这里有两组代码我创建了:
代码选项1:
Intent EMail = ShareCompat.IntentBuilder.from(this)
                   .setType("message/rfc822")
                   .setSubject("Emailing: File Attached")
                   .setText("Hello")
                   .setStream(contentUri)
                   .setChooserTitle("Send via EMail").getIntent();
startActivity(Intent.createChooser(EMail, "Send via EMail"));

上面的代码显示了一个选择器,其中有许多应用程序可以处理文件,如下图所示。

Activity Chooser

如果我选择任何电子邮件客户端应用程序或其他应用程序,这个工作得很好。

但问题在于,用户有选择任何应用程序的选项,这不是应用程序的期望行为。因此,我将代码修改如下:

final Intent _Intent = new Intent(Intent.ACTION_SENDTO);
_Intent.setType("text/html");
_Intent.setData(Uri.parse("mailto:"));
_Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
_Intent.putExtra(Intent.EXTRA_STREAM, contentUri);
_Intent.putExtra(android.content.Intent.EXTRA_SUBJECT,
        "Emailing: File Attached");
_Intent.putExtra(android.content.Intent.EXTRA_TEXT,
        "Hello");
startActivity(Intent.createChooser(_Intent, "Send via EMail"));

这是代码的结果:

only email clients

但是,现在的问题是我无法从内容提供者(FileProvider)发送文件。在选择后,电子邮件客户端显示以下消息:

enter image description here

在上述列表中的任何客户端中,都无法将文件附加到电子邮件中。

如果有人能帮我解决这个问题,我将非常感激。我认为,我已经尝试了所有可能的情况,通过更改MIME类型、以不同方式设置内容、设置数据流等,但无法获得所需的结果。

如果您需要其他详细信息,请告诉我。

再次感谢您的帮助。


你需要编写ContentProvider,该ContentProvider将为客户端提供InputStream,而你已经传递了ContentUri。 - Mohammed Rampurawala
@zeus,你能否帮我写一两行代码?我在这方面有点菜。 - Anant Anand Gupta
我不确定这是否对你有所帮助,但是看一下这个链接:https://dev59.com/gWsz5IYBdhLWcg3wy689#31470694。file://曾经困扰了我,没有它的话可以工作,但那是一个非内容提供者的文件。你可以尝试不使用content:/。这只是我的猜测.. ;) - cgr
3个回答

1
你需要编写ContentProvider,为客户端提供InputStream,以便向其传递ContentUri或者如果文件存在于SdCard或内部存储器中,则可以直接提供文件路径,因为您需要处理uri并传递InputStream。注意:ExtraStream最适合那些不在设备上的文件,即需要从互联网访问的文件。
public class SampleContentProvider extends ContentProvider implements ContentProvider.PipeDataWriter<InputStream> {

    static final UriMatcher uriMatcher;


    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        //Uri matcher for different 

    }

    /**
     * Database specific constant declarations
     */
    private SQLiteDatabase db;


    @Override
    public boolean onCreate() {
        return true;
    }


    @Override
    public Uri insert(Uri uri, ContentValues values) {

        throw new SQLException("Insert operation not supported for  " + uri);
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        //condition just for files. You can try something else
        if (uri.toString().contains("files")) {

            //you get the file name
            String lastSegment = uri.getLastPathSegment();

            if (projection == null) {
                projection = new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
            }

            File file = //Code to read the file as u have the directory, just get the file from the file name obtained from the uri


            if (null == file) {
                throw new IllegalArgumentException("Unknown File for Uri " + uri);
            }
            String[] cols = new String[projection.length];
            Object[] values = new Object[projection.length];
            int i = 0;
            for (String col : projection) {
                if (OpenableColumns.DISPLAY_NAME.equals(col)) {
                    cols[i] = OpenableColumns.DISPLAY_NAME;
                    values[i++] = //file name;
                } else if (OpenableColumns.SIZE.equals(col)) {
                    cols[i] = OpenableColumns.SIZE;
                    values[i++] = //file size;
                }
            }

            cols = copyOf(cols, i);
            values = copyOf(values, i);

            final MatrixCursor cursor = new MatrixCursor(cols, 1);
            cursor.addRow(values);
            return cursor;
        }

        return super.query(uri, projection, selection, selectionArgs, sortOrder);

    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return super.delete(uri, selection, selectionArgs);

    }


    private static String[] copyOf(String[] original, int newLength) {
        final String[] result = new String[newLength];
        System.arraycopy(original, 0, result, 0, newLength);
        return result;
    }

    private static Object[] copyOf(Object[] original, int newLength) {
        final Object[] result = new Object[newLength];
        System.arraycopy(original, 0, result, 0, newLength);
        return result;
    }

    @Nullable
    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {

        File file = //read the file
        if (file != null) {
            try {
                StrictMode.ThreadPolicy tp = StrictMode.ThreadPolicy.LAX;
                StrictMode.setThreadPolicy(tp);   
                InputStream in = //Code to get the inputstream;
                // Start a new thread that pipes the stream data back to the caller.
                return openPipeHelper(uri, null, null, in, this);
            } catch (IOException e) {
                FileNotFoundException fnf = new FileNotFoundException("Unable to open " + uri);
                throw fnf;
            }
        }

        throw new IllegalArgumentException("Unknown URI " + uri);
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return super.update(uri, values, selection, selectionArgs);
    }

    @Override
    public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
                                Bundle opts, InputStream args) {
        // Transfer data from the asset to the pipe the client is reading.
        byte[] buffer = new byte[8192];
        int n;
        FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
        try {
            while ((n = args.read(buffer)) >= 0) {
                fout.write(buffer, 0, n);
            }
        } catch (IOException e) {
        } finally {
            try {
                args.close();
            } catch (IOException e) {
            }
            try {
                fout.close();
            } catch (IOException e) {
            }
        }
    }

}

嗨@Zeus,我猜你分享给我的那段代码应该是我正在寻找的正确代码。但由于我无法填写您提供的代码中的空白和占位符,因此我将无法对其作证或支持。不过还是感谢您提供这段代码。我非常感激您在这里的努力。 - Anant Anand Gupta

0

试着使用这段代码片段。

Intent testIntent = new Intent(Intent.ACTION_VIEW);
                    Uri data = Uri.parse("mailto:?subject=" + "Feedback" + "&body=" + "Write Feedback here....." + "&to=" + "someone@example.com");
                    testIntent.setData(data);
                    startActivity(testIntent);

0

我决定将文件从内部应用存储复制到外部应用存储(而不是外部公共存储),然后从那里分享文件。但令我有些惊讶的是,FileProvider 能够与系统中的任何东西共享内部文件存储中的文件,但在我想要筛选只有电子邮件客户端的意图时却无法实现。

对于初学者来说,实现自定义提供程序有点困难。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接