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

Я следил за этим учебным пособием Google, чтобы начать собирать изображение с помощью new Intent(MediaStore.ACTION_IMAGE_CAPTURE). В учебнике рекомендуется использовать общий каталог с getExternalStoragePublicDirectory, который отлично подходит для моего приложения. Но вместо этого их пример использует getExternalFilesDir. Затем передать URI файла в намерение с помощью MediaStore.EXTRA_OUTPUT я получить URI содержимого, потому что я хотел бы настроить таргетинг на Android N. Перед тем, как настроить NI, просто передайте файл://URI и все, кроме Google, были счастливы. Теперь на N я начал получать 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? Все параметры, которые я могу найти, предназначены для частных каталогов.

Ответы

Ответ 1

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() при вызове с помощью среды .DIRECTORY_PICTURES. Убедитесь, что вы заменили com.example.package.name фактическим именем пакета вашего приложения.

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

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

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

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

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

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

Ответ 2

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

  • Создайте файл следующим образом:

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

    <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);