还授予 Uri 读取权限,以访问在浏览器中使用 content:/// 方案打开的 html 链接的文件
Grant Uri read permission also to files linked by html opened in browser with content:/// scheme
要在网络浏览器中打开文件,并避免来自 Oreo 的 FileUriExposedException
,我使用 FileProvider
和临时 URI 权限正确共享文件。
问题是要打开的 html 文件 包含一个 link 到另一个伴随文件 ,它与 file:///[...]
一起工作并且固定读取外部文件的权限目标应用程序中的内存,但不适用于 content:///[...]
因为临时权限当然只授予第一个文件而不是第二个文件。
有什么方法可以授予另一个文件临时读取权限吗?在用户 select 他们实际想要使用的应用程序之前,无需手动向每个合适的应用程序授予其他文件的权限?
目前的代码,使用各种浏览器的 itents:
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()
?):如果原始应用程序在浏览器仍处于打开状态时关闭,会发生什么情况? (当您使用意图时,当目标应用程序展开堆栈时,权限将被撤销。)文档指出权限在设备重新启动之前有效。这似乎比其他选择更好。意见?
有没有办法只针对用户实际选择的应用执行此操作? (无需重新实现整个选择器。)
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
}
}
要在网络浏览器中打开文件,并避免来自 Oreo 的 FileUriExposedException
,我使用 FileProvider
和临时 URI 权限正确共享文件。
问题是要打开的 html 文件 包含一个 link 到另一个伴随文件 ,它与 file:///[...]
一起工作并且固定读取外部文件的权限目标应用程序中的内存,但不适用于 content:///[...]
因为临时权限当然只授予第一个文件而不是第二个文件。
有什么方法可以授予另一个文件临时读取权限吗?在用户 select 他们实际想要使用的应用程序之前,无需手动向每个合适的应用程序授予其他文件的权限?
目前的代码,使用各种浏览器的 itents:
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()
?):如果原始应用程序在浏览器仍处于打开状态时关闭,会发生什么情况? (当您使用意图时,当目标应用程序展开堆栈时,权限将被撤销。)文档指出权限在设备重新启动之前有效。这似乎比其他选择更好。意见?
有没有办法只针对用户实际选择的应用执行此操作? (无需重新实现整个选择器。)
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
}
}