Предоставьте Uri разрешение на чтение также для файлов, связанных html, открытых в браузере с схемой content:///

Чтобы открыть файл в веб-браузере и избежать FileUriExposedException от Oreo, я правильно делюсь файлами, используя FileProvider и временные разрешения URI. Проблема в том, что открываемый HTML-файл содержит ссылку на другой сопутствующий файл, который отлично работал с file:///[...] и фиксированным разрешением на чтение внешней памяти в целевом приложении, но не работает с content:///[...] из-за временные разрешения курса предоставляются только первому файлу, а не второму.

Есть ли способ предоставить временное разрешение на чтение для другого файла? Не прибегая к ручному предоставлению разрешения на другой файл каждому подходящему приложению, прежде чем пользователь выберет приложение, которое он действительно хочет использовать?

Настоящий код, использующий элементы для различных браузеров:

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

person GozzoMan    schedule 14.06.2018    source источник
comment
Вам лучше изменить тему на: Chrome и другие браузеры не показывают изображения из тега img/src, если html-страница загружается из схемы содержимого.   -  person greenapps    schedule 14.06.2018
comment
Да. Я тоже это видел. Если html-страница открывается с помощью Storage Access Framework и получает схему содержимого, то браузеры могут отображать текст страницы. Но они не могут получить относительные файлы изображений, упомянутые в атрибуте src тега img. Стыд!   -  person greenapps    schedule 14.06.2018


Ответы (1)


Что ж, без ответов и без лучших вариантов лучшее, что я мог придумать, — это предварительно авторизовать любой доступный браузер для чтения связанного файла.

Что касается 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
    }
}
person GozzoMan    schedule 30.07.2018