android - 还授予 Uri 对在浏览器中使用 content:/// 方案打开的 html 链接的文件的读取权限
问题描述
为了在网络浏览器中打开文件并避免来自 Oreo,我使用和临时 URI 权限FileUriExposedException
正确共享文件。FileProvider
问题是要打开的 html 文件包含指向另一个伴随文件的链接,该文件可以正常工作file:///[...]
并修复了在目标应用程序中读取外部内存的权限,但不能正常工作,content:///[...]
因为临时权限仅授予第一个文件而不是第二个。
有没有办法也可以授予另一个文件的临时读取权限?在用户选择他们实际想要使用的应用程序之前,不求助于手动将其他文件的权限授予每个合适的应用程序?
目前的代码,使用各种浏览器的items:
protected Uri getUriFromFile(File file)
{
return FileProvider.getUriForFile(
this,
"com.whatever.example.fileprovider",
file);
}
protected void openInWebBroser(File file, File linkedFile)
{
Uri uri = getUriFromFile(file);
Uri linkedUri = getUriFromFile(linkedFile);
// Firefox and html viewer
Intent webIntent1 = new Intent(Intent.ACTION_VIEW);
webIntent1.setDataAndType(uri, "text/html");
webIntent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// TODO somehow add also linkedUri, not to be open right now but to be added to permitted???
// Chrome
Intent webIntent2 = new Intent(Intent.ACTION_VIEW);
webIntent2.setDataAndType(uri, "multipart/related");
webIntent2.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Default android browser
Intent webIntent3 = new Intent(Intent.ACTION_VIEW);
webIntent3.setDataAndType(uri, "text/html");
webIntent3.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
webIntent3.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if(getPackageManager().queryIntentActivities(webIntent1, 0).size() > 0)
{
Intent chooserIntent = Intent.createChooser(webIntent1, null);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { webIntent2, webIntent3 });
startActivity(chooserIntent);
}
else if(getPackageManager().queryIntentActivities(webIntent2, 0).size() > 0)
{
Intent chooserIntent = Intent.createChooser(webIntent2, null);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { webIntent3 });
startActivity(chooserIntent);
}
else if(getPackageManager().queryIntentActivities(webIntent3, 0).size() > 0)
{
Intent chooserIntent = Intent.createChooser(webIntent3, null);
startActivity(chooserIntent);
}
else
{
// ... error management
}
}
解决方案
好吧,没有答案也没有更好的选择,我能想到的最好的办法是预先授权任何可用的浏览器来读取链接的文件。
至于revokeUriPermission()
,虽然文档坚持您必须调用它,但从原始应用程序调用它似乎并不正确(例如,onDestroy()
何时isFinishing()
?):如果原始应用程序在浏览器仍然打开时关闭会发生什么?(当您使用 Intent 时,当目标应用程序展开堆栈时,权限将被撤销。)文档指出权限在设备重新启动之前有效。这似乎比其他选择更好。意见?
有没有办法只为用户实际选择的应用程序这样做?(无需重新实现整个选择器。)
protected Uri getUriFromFile(File file)
{
return FileProvider.getUriForFile(
this,
"com.whatever.example.fileprovider",
file);
}
protected void openInWebBroser(File file, File linkedFile)
{
Uri uri = getUriFromFile(file);
Uri linkedUri = null;
if( linkedFile!= null )
linkedUri = getUriFromFile(linkedFile);
// Firefox and html viewer
Intent webIntent1 = new Intent(Intent.ACTION_VIEW);
webIntent1.setDataAndType(uri, "text/html");
webIntent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Chrome
Intent webIntent2 = new Intent(Intent.ACTION_VIEW);
webIntent2.setDataAndType(uri, "multipart/related");
webIntent2.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Default android browser
Intent webIntent3 = new Intent(Intent.ACTION_VIEW);
webIntent3.setDataAndType(uri, "text/html");
webIntent3.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
webIntent3.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Pre-grant access to linkedUri for any candidate
if( linkedUri != null )
{
Intent[] intents = new Intent[]{ webIntent1, webIntent2, webIntent3 };
for( Intent intent : intents )
for( ResolveInfo info : getPackageManager ().queryIntentActivities(intent, 0) )
grantUriPermission(info.activityInfo.packageName, linkedUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
if(getPackageManager().queryIntentActivities(webIntent1, 0).size() > 0)
{
Intent chooserIntent = Intent.createChooser(webIntent1, null);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { webIntent2, webIntent3 });
startActivity(chooserIntent);
}
else if(getPackageManager().queryIntentActivities(webIntent2, 0).size() > 0)
{
Intent chooserIntent = Intent.createChooser(webIntent2, null);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { webIntent3 });
startActivity(chooserIntent);
}
else if(getPackageManager().queryIntentActivities(webIntent3, 0).size() > 0)
{
Intent chooserIntent = Intent.createChooser(webIntent3, null);
startActivity(chooserIntent);
}
else
{
// ... error management
}
}
推荐阅读
- bash - 用另一个名称替换输出的列并将其保存为 .CSV 文件
- python - 搜索字符串以查找字符对的最佳实践方法是什么?
- css - 为什么当宽度为 736px 时 svg LogoMonniMobile 不可见?
- alpine.js - 在 x-data 之外管理 alpine 组件
- sql - 如何将 X 项转换为数组和循环
- java - 如何处理 java 异常 InputMismatchException 以及自定义异常?
- java - Mapstruct - Kotlin - 不可变嵌套对象 - 传播父值
- google-play-console - 您无法删除谷歌播放控制台上的所有生产 APK 和 Android App Bundles 错误
- mysql - 插入查询在我的本地工作正常,但不能在我的实时数据库(Laravel/MySQL)上执行
- php - 当服务器和客户端在同一台服务器上时,在 FastCGI 上使用 PHP+CURL 时出现问题