将位于 /res/raw/ 的 .mp3 文件分享到 WhatsApp

Share .mp3 file located in /res/raw/ to WhatsApp

我正在 Android (API 29) 中编写铃声应用程序,我想通过 WhatsApp 分享以 MP3 格式存储在 /res/raw/ 文件夹中的声音。我正在使用此代码:

    Intent share = new Intent(Intent.ACTION_SEND);
    Uri path = Uri.parse("android.resource://"+ getPackageName() + "/" + R.raw.yeah);
    share.setPackage("com.whatsapp");
    share.setType("audio/mp3");
    share.putExtra(Intent.EXTRA_STREAM, path);
    startActivity(Intent.createChooser(share, "Share"));

但每次我 select 聊天发送音频文件时,我都会收到 WhatsApp 错误消息,告诉我无法共享文件。我已经看到其他应用程序做我想做的事情 WITHOUT 请求写入外部存储的权限。任何线索是如何做到的?

But every time I select a chat to send the audio file, I get a WhatsApp error telling me the file could not be shared

很少有应用能很好地支持晦涩的 android.resource 方案。另外,EXTRA_STREAM is documented to take a content Uri,不是 android.resource Uri.

Any clues how this has been done?

将原始资源复制到 internal storage, such as in getCacheDir(). You can use the Resources object you get from calling getResources() a Context to get access to the raw resource (using openRawResource()). Then, configure FileProvider 上的文件以从那里提供服务,并使用 FileProvider.getUriForFile() 获取 Uri 以与 ACTION_SEND 一起使用。请务必在 Intent.

上添加 FLAG_GRANT_READ_URI_PERMISSION

This sample Java app (and its Kotlin equivalent) 演示基本技术。就我而言,我将 PDF 存储为资产,而不是将 MP3 存储为原始资源,因此这部分需要进行一些小的调整。

或者,您可以从头开始编写您自己的 ContentProvider 来为您的原始资源提供服务,然后在您的 Intent.

中使用该提供商支持的 Uri

最后我设法通过混合 CommonsWare 答案解决了问题, and 。提前感谢大家!

一步一步解决:

首先,让我们从一些XML开始。

  1. 配置 AndroidManifest.xml 并在 <application> 部分中添加以下行。我将它们放在我的 </activity></application> 结束标记之间,但请将此位置作为我个人的选择:根据您的清单布局,它可能不适合您。

AndroidManifest.xml

<provider
   android:name="androidx.core.content.FileProvider"
   android:authorities="${applicationId}.provider"
   android:exported="false"
   android:grantUriPermissions="true">
   <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/file_paths" />
</provider>

因为我使用的是 API 29,所以我是 AndroidX 图书馆。如果您也想使用它,请考虑 运行 AndroidX 迁移向导,方法是在 Android Studio 中单击 Refactor > Migrate to AndroidX...,风险自负。

  1. 现在在/res/xml中创建一个名为file_paths.xml的文件并按如下方式填写:

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path name="my_sounds" path="/"/>
</paths>

请注意 my_sounds 名称是任意的。 path 字段中的 / 是您的文件将存储在缓存路径中的位置。为了方便,我只是让它那样。如果您不想使用缓存路径,here 是您可以使用的可用标签的完整列表。

现在我们将返回 Java 并开始编写处理共享的方法。首先,我们需要将资源文件夹中的文件复制到 File 对象。但是,这个 File 需要指向我们在 XML 部分配置的文件提供程序创建的路径。分任务吧:

  1. 用你的文件数据创建一个 InputStream 并用辅助程序填充一个 File

public void handleMediaSend(int position)

File sound;
try {
    InputStream inputStream = getResources().openRawResource(sounds.get(position).getSound()); // equivalent to R.raw.yoursound
    sound = File.createTempFile("sound", ".mp3");
    copyFile(inputStream, new FileOutputStream(sound));
} catch (IOException e) {
    throw new RuntimeException("Can't create temp file", e);
}

辅助程序:

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);
}

现在您的资源已成功传输到内部存储中的缓存目录(使用调试器查看哪个目录)。

  1. 获取 Uri 并分享 File
final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
Uri uri = getUriForFile(getApplicationContext(), AUTHORITY, sound);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("audio/mp3"); // or whatever.
share.putExtra(Intent.EXTRA_STREAM, uri);
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(share, "Share"));

将所有这些放在一起,我们将以这种方法结束:

完整方法

public void handleMediaSend(int position) { // Depends on your implementation.
    File sound;
    try {
        InputStream inputStream = getResources().openRawResource(sounds.get(position).getSound()); // equivalent to R.raw.yoursound
        sound = File.createTempFile("sound", ".mp3");
        copyFile(inputStream, new FileOutputStream(sound));
    } catch (IOException e) {
        throw new RuntimeException("Can't create temp file", e);
    }

    final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
    Uri uri = getUriForFile(getApplicationContext(), AUTHORITY, sound);
    Intent share = new Intent(Intent.ACTION_SEND);
    share.setType("audio/mp3"); // or whatever.
    share.putExtra(Intent.EXTRA_STREAM, uri);
    share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(Intent.createChooser(share, "Share"));
}