Android 尝试使用cwac提供程序发送pdf

Android 尝试使用cwac提供程序发送pdf,android,commonsware-cwac,share-intent,Android,Commonsware Cwac,Share Intent,我无法让cwac正常工作,查看演示代码也没有帮助。我只是想通过共享意图导出pdf。当前输出为“无法附加空文件”错误。但该文件确实存在,我无法判断问题是否出在文件名、位置或cwac提供程序的使用上 下面是我如何设置提供者的 <provider android:name="com.commonsware.cwac.provider.StreamProvider" android:authorities="com.anothergamedesign

我无法让cwac正常工作,查看演示代码也没有帮助。我只是想通过共享意图导出pdf。当前输出为“无法附加空文件”错误。但该文件确实存在,我无法判断问题是否出在文件名、位置或cwac提供程序的使用上

下面是我如何设置提供者的

        <provider
        android:name="com.commonsware.cwac.provider.StreamProvider"
        android:authorities="com.anothergamedesigner.CatVimeoTest.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="com.commonsware.cwac.provider.STREAM_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
    </provider>
方法定义:

private Intent getOpenPDFShareIntent(String name){
    Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
    shareIntent.setType("application/pdf");

    shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {""});
    shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.default_share_subject));
    shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, getResources().getString(R.string.default_share_text));

    shareIntent.putExtra(Intent.EXTRA_STREAM, getURI());
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    shareIntent.setType("text/plain");
    return shareIntent;
}

private Uri getURI(){
    String path = ASSET_PATHS + pdfName;
    return(PROVIDER
            .buildUpon()
            .appendPath(StreamProvider.getUriPrefix(AUTHORITY))
            .appendPath(path)
            .build());

}
在getURI()中,System.out.println(path)的返回示例是:“assets/my.pdf”

代码通过菜单按钮运行,该按钮在选择时:

Intent shareIntent = getOpenPDFShareIntent(pdfName);
StartActivity(Intent.createChooser(shareIntent, getResources().getString(R.string.contact_send_mail));
编辑: 正在尝试删除空前缀:

    private Uri getURI(){
    //String path = ASSET_PATHS + pdfName;

    if(StreamProvider.getUriPrefix(AUTHORITY) != null){
        return(PROVIDER
                .buildUpon()
                .appendPath(StreamProvider.getUriPrefix(AUTHORITY))
                .appendPath(ASSET_PATHS)
                .appendPath(pdfName)
                .build());
    } else{
        return(PROVIDER
                .buildUpon()
                .appendPath(ASSET_PATHS)
                .appendPath(pdfName)
                .build());
    }
}
编辑2-使用DocumentFile进行测试:

我从另一个SO答案中使用了这些新方法,并对其进行了修改以返回文件

private File CopyReadAssets()
{
    AssetManager assetManager = getAssets();

    InputStream in = null;
    OutputStream out = null;
    File file = new File(getFilesDir(), "my.pdf");
    try
    {
        in = assetManager.open("my.pdf");
        out = openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);

        copyFile(in, out);
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e)
    {
        Log.e("tag", e.getMessage());
    }

    return file;
}

private void copyFile(InputStream in, OutputStream out) throws IOException
{
    byte[] buffer = new byte[1024];
    int read;
    while ((read = in.read(buffer)) != -1)
    {
        out.write(buffer, 0, read);
    }
}
然后,我使用以下方法进行测试:

DocumentFile aFile = DocumentFile.fromFile(CopyReadAssets());
DocumentFile file = DocumentFile.fromSingleUri(this, getURI());
System.out.println(aFile.getName());
System.out.println(aFile.length());
System.out.println(file.getName());
System.out.println(file.length());
对于文件,它返回“my.pdf”和“33528”;对于文件,它返回“my.pdf”和“FileNotFoundException”

content://com.anothergamedesigner.CatVimeoTest/null/assets%2FCEGA%20OnBoard%20Su‌​pport%201.6.compressed.pdf
我可以看到这里有两个问题

首先,
StreamProvider.getUriPrefix(AUTHORITY)
正在返回
null
。我不知道为什么,因为您似乎没有子类化
StreamProvider
。也就是说,您应该检查
null
并跳过
appendPath()
语句(如果它是
null

其次,使用两条
appendPath()
语句,而不是
ASSET\u path+pdfName
。这将防止
/
转换为
%2F

请注意,我没有尝试过名称中带有空格的文件,也没有尝试过像文件名中那样必须转义的任何其他文件。可能存在与此相关的bug。如果更改上述两项没有帮助,请尝试将PDF临时重命名为简单的名称。如果这样做有效,我有一个bug需要修复

我可以看到这里有两个问题

首先,
StreamProvider.getUriPrefix(AUTHORITY)
正在返回
null
。我不知道为什么,因为您似乎没有子类化
StreamProvider
。也就是说,您应该检查
null
并跳过
appendPath()
语句(如果它是
null

其次,使用两条
appendPath()
语句,而不是
ASSET\u path+pdfName
。这将防止
/
转换为
%2F

请注意,我没有尝试过名称中带有空格的文件,也没有尝试过像文件名中那样必须转义的任何其他文件。可能存在与此相关的bug。如果更改上述两项没有帮助,请尝试将PDF临时重命名为简单的名称。如果这样做有效,我有一个bug需要修复。

最终代码

问题似乎在于资产路径。由于某些原因,将pdfName附加到“资产”中被证明是有问题的。因此,我选择从final更改ASSET_路径,然后在onCreate方法中设置它。这将是一种方法,或者您可以在不同的PDF中硬编码,例如在演示项目中

public class PDFViewActivity extends AppCompatActivity {

private String pdfName;
...

private static final String AUTHORITY = "com.your.provider";
private static final Uri PROVIDER = Uri.parse("content://"+AUTHORITY);
private static String ASSET_PATHS;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Bundle extras = getIntent().getExtras();
    pdfName = extras.getString("pdfName");
    ...

    ASSET_PATHS = "assets/" + pdfName;

    // Place an PDFViewFragment as our content pane
    PDFViewFragment f = new PDFViewFragment();
    getSupportFragmentManager().beginTransaction().add(android.R.id.content, f).commit();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_pdf, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.menu_button_share:
            Intent shareIntent = getOpenPDFShareIntent();
            startActivity(Intent.createChooser(shareIntent, "Send Email"));
            return true;
        ...
        default:
            return super.onOptionsItemSelected(item);
    }
}

private Intent getOpenPDFShareIntent(){
    Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setType("application/pdf");

    shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {""});
    shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.default_share_subject));
    shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, getResources().getString(R.string.default_share_text));
    shareIntent.putExtra(Intent.EXTRA_STREAM, getURI());
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    shareIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

    return shareIntent;
}

private Uri getURI(){
    return(PROVIDER
            .buildUpon()
            .appendPath(StreamProvider.getUriPrefix(AUTHORITY))
            .appendPath(ASSET_PATHS)
            .build());
}
}

最终代码

问题似乎在于资产路径。由于某些原因,将pdfName附加到“资产”中被证明是有问题的。因此,我选择从final更改ASSET_路径,然后在onCreate方法中设置它。这将是一种方法,或者您可以在不同的PDF中硬编码,例如在演示项目中

public class PDFViewActivity extends AppCompatActivity {

private String pdfName;
...

private static final String AUTHORITY = "com.your.provider";
private static final Uri PROVIDER = Uri.parse("content://"+AUTHORITY);
private static String ASSET_PATHS;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Bundle extras = getIntent().getExtras();
    pdfName = extras.getString("pdfName");
    ...

    ASSET_PATHS = "assets/" + pdfName;

    // Place an PDFViewFragment as our content pane
    PDFViewFragment f = new PDFViewFragment();
    getSupportFragmentManager().beginTransaction().add(android.R.id.content, f).commit();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_pdf, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.menu_button_share:
            Intent shareIntent = getOpenPDFShareIntent();
            startActivity(Intent.createChooser(shareIntent, "Send Email"));
            return true;
        ...
        default:
            return super.onOptionsItemSelected(item);
    }
}

private Intent getOpenPDFShareIntent(){
    Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setType("application/pdf");

    shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {""});
    shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.default_share_subject));
    shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, getResources().getString(R.string.default_share_text));
    shareIntent.putExtra(Intent.EXTRA_STREAM, getURI());
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    shareIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

    return shareIntent;
}

private Uri getURI(){
    return(PROVIDER
            .buildUpon()
            .appendPath(StreamProvider.getUriPrefix(AUTHORITY))
            .appendPath(ASSET_PATHS)
            .build());
}

}

我将首先修复您的MIME类型。您没有共享纯文本。您正在共享
application/pdf
,并且正在调用
setType()
两次。另外,在你开始工作之前,不要使用额外的文本
ACTION\u SEND
支持
EXTRA\u TEXT
EXTRA\u STREAM
,因此根据
ACTION\u SEND
活动的实施情况,您可能会得到一个或另一个,但不能同时得到两个。除此之外,您从
getURI()
中得到的整个
Uri
是什么样子的?我已经注释掉了这些行,是的,注意到了setType并删除了它。同样的问题,但最好按照你的建议去做。URI是:content://com.anothergamedesigner.CatVimeoTest/null/assets%2FCEGA%20OnBoard%20Support%201.6.compressed.pdfAny 知道空部分是如何产生的吗?我将从修复MIME类型开始。您没有共享纯文本。您正在共享
application/pdf
,并且正在调用
setType()
两次。另外,在你开始工作之前,不要使用额外的文本
ACTION\u SEND
支持
EXTRA\u TEXT
EXTRA\u STREAM
,因此根据
ACTION\u SEND
活动的实施情况,您可能会得到一个或另一个,但不能同时得到两个。除此之外,您从
getURI()
中得到的整个
Uri
是什么样子的?我已经注释掉了这些行,是的,注意到了setType并删除了它。同样的问题,但最好按照你的建议去做。URI是:content://com.anothergamedesigner.CatVimeoTest/null/assets%2FCEGA%20OnBoard%20Support%201.6.compressed.pdfAny 知道空部分是如何产生的吗?我试着按照你说的做,但我注意到的第一件事是返回的URI是空的"content://com.anothergamedesigner.CatVimeoTest/assets%2F/CEGA%20OnBoard%20Support%201.6.compressed.pdf“因此,/仍在被转换。@NappyXIII:抱歉,您还应该从
资产路径中删除该斜杠。
。是的,也意识到了这一点,只是修复了它。所以我尝试硬编码my.pdf并将其中一个pdf重命名为该格式。我的新URI是:”content://com.anothergamedesigner.CatVimeoTest/assets/my.pdf“我仍然得到一个空文件。所以我下一个猜测是我必须解决URIPrefix问题,因为似乎不是空格导致了问题。我也尝试将my.pdf放在子目录中,以查看(path=”“)是否存在错误。”路径xml的一部分,没有骰子。我试着按照你说的做,但我注意到的第一件事是URI retu
public class PDFViewActivity extends AppCompatActivity {

private String pdfName;
...

private static final String AUTHORITY = "com.your.provider";
private static final Uri PROVIDER = Uri.parse("content://"+AUTHORITY);
private static String ASSET_PATHS;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Bundle extras = getIntent().getExtras();
    pdfName = extras.getString("pdfName");
    ...

    ASSET_PATHS = "assets/" + pdfName;

    // Place an PDFViewFragment as our content pane
    PDFViewFragment f = new PDFViewFragment();
    getSupportFragmentManager().beginTransaction().add(android.R.id.content, f).commit();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_pdf, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.menu_button_share:
            Intent shareIntent = getOpenPDFShareIntent();
            startActivity(Intent.createChooser(shareIntent, "Send Email"));
            return true;
        ...
        default:
            return super.onOptionsItemSelected(item);
    }
}

private Intent getOpenPDFShareIntent(){
    Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setType("application/pdf");

    shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {""});
    shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.default_share_subject));
    shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, getResources().getString(R.string.default_share_text));
    shareIntent.putExtra(Intent.EXTRA_STREAM, getURI());
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    shareIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

    return shareIntent;
}

private Uri getURI(){
    return(PROVIDER
            .buildUpon()
            .appendPath(StreamProvider.getUriPrefix(AUTHORITY))
            .appendPath(ASSET_PATHS)
            .build());
}