Android: Как получить контент:// URI для файла в каталоге PUBLIC внешнего хранилища

Я следовал этому уроку Google, чтобы начать собирать изображение с new Intent(MediaStore.ACTION_IMAGE_CAPTURE), Учебник рекомендует использовать публичный каталог с getExternalStoragePublicDirectory который будет отлично работать для моего приложения. Но тогда их пример вместо этого использует getExternalFilesDir, Затем передать URI файла в намерение с MediaStore.EXTRA_OUTPUT Мне нужно получить URI контента, потому что я хочу нацелить на Android N. Перед нацеливанием на N I нужно просто передать файл:// URI и все, кроме Google, были счастливы. Теперь на N I начали получать FileUriExposedException это кажется не очень благоприятным.

Итак, учитывая, что у меня есть файл, как это...

private File createImageFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyAppFolder");
    if (!storageDir.exists() && !storageDir.mkdir())
        Log.w(TAG, "Couldn't create photo folder: " + storageDir.getAbsolutePath());
    File image = new File(storageDir, timeStamp + ".jpg");
    mCurrentPhotoPath = image.getAbsolutePath();
    return image;
}

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

Я пробовал что-то вроде

takePictureIntent.putExtra(
    MediaStore.EXTRA_OUTPUT, 
    FileProvider.getUriForFile(this, MediaStore.AUTHORITY, createImageFile()));

но это просто бросает

IllegalArgumentException: отсутствуют метаданные android.support.FILE_PROVIDER_PATHS

Правильно ли это Управление? Если я должен использовать свой собственный поставщик для предоставления доступа к общедоступному файлу, какой путь я могу указать в моих метаданных FILE_PROVIDER_PATHS? Все варианты, которые я могу найти, предназначены для частных каталогов.

2 ответа

TL:DR <external-path name="MyAppFolder" path="." /> работает, но безопасно ли?

Вам нужно будет сделать свой собственный FileProvider, Стоит прочитать его документацию, но вот краткое изложение.

Как упоминалось @CommonsWare, вам нужно заменить MediaStore.AUTHORITY из вашего примера с "com.example.fileprovider", который является властью, которую вы определите в своем манифесте так...

В пределах <application> тег в AndroidManifest, введите следующее:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

Исключение, которое вы получаете, связано с отсутствием этого <meta-data> тег, который ссылается на XML-файл со всеми путями, которые ваш FileProvider разрешено трогать. Но это то, что идет в этом файле, что я боролся с собой.

В конце урока, который вы связали, в конце раздела " Сохранить полноразмерное фото " приведен пример, который Google дает для этого. file_paths.xml является:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>

"my_images" будет заменен на "MyAppFolder" в вашем примере. В этом уроке Google утверждает

Компонент пути соответствует пути, который возвращается getExternalFilesDir() при вызове с помощью Environment.DIRECTORY_PICTURES. Убедитесь, что вы заменили com.example.package.name на фактическое имя пакета вашего приложения.

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

java.lang.IllegalArgumentException: не удалось найти настроенный корень, который содержит /storage/emulated/0/Pictures/MyAppFolder/ndomfilename Event.jpg

Я оставлю это здесь для дальнейшего использования и перейду к ответу на ваш большой вопрос - какой путь вы можете указать, чтобы другие приложения могли взаимодействовать с файлом в общедоступном каталоге Pictures, созданном вашим приложением с целевым уровнем API 24+?

Как сказал @CommonsWare, "детали вашего желаемого подкаталога зависят от устройства", поэтому вы не можете объявить путь к общедоступному каталогу Pictures, но я нашел способ, который работает.

<external-path name="MyAppFolder" path="." /> позволит вашему FileProvider предоставить URI контента другим приложениям (таким как Камера), которые затем могут читать и записывать в файл, к которому он обращается.

Если есть какие-либо опасности или недостатки в этом, я хотел бы услышать их.

Вот что я использую в своем приложении для общедоступного каталога Pictures.

  1. Создайте файл так:

    File theFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "YOUR_APP_NAME" + File.separator + "YOUR_FILE_NAME");
    
  2. Укажите следующий путь в метаданных провайдера файлов:

    <paths>
        <external-path name="secure_name" path="Pictures/YOUR_APP_NAME" />
    </paths>
    

"secure_name" используется, чтобы скрыть имя подкаталога (в данном случае "YOUR_APP_NAME"), которым вы делитесь по соображениям безопасности.

"Картинки" соответствует Environment.DIRECTORY_PICTURES,

  1. получать Uri отправить с Intent:

    final Uri theUri = FileProvider.getUriForFile(activity,
                        getString(R.string.fileprovider_authorities),
                        theFile);
    theIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    theIntent.putExtra(MediaStore.EXTRA_OUTPUT, theUri);
    
Другие вопросы по тегам