Как программно установить звук в качестве мелодии звонка выше Android N

В прошлом мы могли использовать приведенный ниже код, чтобы установить аудиофайл в качестве мелодии звонка:

ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, musicFile.getAbsolutePath());
values.put(MediaStore.MediaColumns.TITLE, "my music");
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");
values.put(MediaStore.MediaColumns.SIZE, 215454);
values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
values.put(MediaStore.Audio.Media.IS_NOTIFICATION, false); // true for notification sound
values.put(MediaStore.Audio.Media.IS_ALARM, false);
values.put(MediaStore.Audio.Media.IS_MUSIC, false);

Uri uri = MediaStore.Audio.Media.getContentUriForPath(musicFile.getAbsolutePath());
Strint where = MediaStore.MediaColumns.DATA + "=\""
                + newSoundFile.getAbsolutePath() + "\"";
getContentResolver().delete(uri, where, null);
Uri newUri = getContentResolver().insert(uri, values);
RingtoneManager.setActualDefaultRingtoneUri(
        RingtonesPlaying.this, RingtoneManager.TYPE_RINGTONE, newUri);

Однако, если мы запустим код выше Nougat (7.0, API 24), мы получим SecurityException для getContentResolver().insert(), что у нас нет разрешения MANAGE_DOCUMENTS, которое всегда будет выдаваться, даже если мы объявим это разрешение в AndroidManifest.

Я действительно хочу установить аудиофайл в качестве мелодии звонка, так как хочу, чтобы пользователи моего приложения имели возможность настраивать звук уведомлений. На самом деле мы можем использовать builder.setSound(Uri.fromFile(musicFile)) перед N для Notification, но этот подход также запрещен для N и выдаст FileUriExposedException.


person ywwynm    schedule 02.09.2016    source источник
comment
этот подход также запрещен для N и выдаст FileUriExposedException — используйте FileProvider и FileProvider.getUriForFile() вместо Uri.fromFile().   -  person CommonsWare    schedule 02.09.2016
comment
@CommonsWare Аудиофайл выбирается пользователем, я не хочу ограничивать папки, из которых пользователь может выбирать только свою музыку. Поэтому, если я использую FileProvider.getUriForFile(), я должен указать эти папки, что очень бесполезно в этой ситуации.   -  person ywwynm    schedule 02.09.2016
comment
Аудиофайл выбирается пользователем — когда пользователь выбирает звук из MediaStore, у вас есть Uri к звуку в MediaStore. Я не знаю, насколько долговечна эта Uri. Либо используйте его напрямую, либо сделайте копию звука в getCacheDir() и используйте FileProvider, либо используйте FileProvider с конфигурацией external-path.   -  person CommonsWare    schedule 02.09.2016
comment
@CommonsWare Не могли бы вы опубликовать ответ здесь? Поскольку FileProvider API совершенно новый, я не знаю, как его использовать для достижения своей цели. Я думаю, что есть также много разработчиков, которые тоже мало что знают об этом. Поэтому, если вы можете объяснить это нам, мы будем вам очень благодарны.   -  person ywwynm    schedule 03.09.2016
comment
@CommonsWare Следуя вашему предложению, я использую FileProvider с внешним путем, чтобы установить звук для уведомления, используя NotificationCompat.Builder.setSound(), но это приводит к отказу в разрешении. Должен ли я давать какие-либо разрешения NotificationManager?? И что более важно, почему установка звука уведомления представляет угрозу безопасности? Что я здесь выставляю?   -  person jmart    schedule 07.09.2016
comment
@jmart: но это приводит к отказу в доступе - ммм ... это интересный момент. Обычно мы передаем эти значения Uri через Intents, и мы можем использовать FLAG_GRANT_READ_URI_PERMISSION, чтобы дать ответчику доступ к нашему контенту. Я должен изучить это. почему установка звука уведомления представляет угрозу безопасности? -- это не так, но с FileProvider контент предоставляется по запросу. Контент доступен для чтения не всем желающим.   -  person CommonsWare    schedule 07.09.2016
comment
@jmart: см. ответ, который я только что опубликовал, или моя запись в блоге, откуда пришел ответ для списка обходных путей.   -  person CommonsWare    schedule 07.09.2016