首页 > 解决方案 > 还授予 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
    }
}

标签: androidandroid-intentandroid-fileprovider

解决方案


好吧,没有答案也没有更好的选择,我能想到的最好的办法是预先授权任何可用的浏览器来读取链接的文件。

至于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
    }
}

推荐阅读