将位于 /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开始。
- 配置
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...
,风险自负。
- 现在在
/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 部分配置的文件提供程序创建的路径。分任务吧:
- 用你的文件数据创建一个
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);
}
现在您的资源已成功传输到内部存储中的缓存目录(使用调试器查看哪个目录)。
- 获取
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"));
}
我正在 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 答案解决了问题,
一步一步解决:
首先,让我们从一些XML开始。
- 配置
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...
,风险自负。
- 现在在
/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 部分配置的文件提供程序创建的路径。分任务吧:
- 用你的文件数据创建一个
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);
}
现在您的资源已成功传输到内部存储中的缓存目录(使用调试器查看哪个目录)。
- 获取
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"));
}