Звук уведомления Android 7.0 от поставщика файлов Uri не воспроизводится

Я меняю код своего приложения для поддержки Android 7, но в моем NotificationCompat.Builder.setSound(Uri), передающем Uri из FileProvider, уведомление не воспроизводит звук, в Android 6 использование Uri.fromFile() работало правильно.

Файл mp3 находится в:

/Animeflv/cache/.sounds/

Это мой код уведомления:

knf.animeflv.RequestBackground

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_not_r)
.setContentTitle(NotTit)
.setContentText(mess);
...
mBuilder.setVibrate(new long[]{100, 200, 100, 500});
mBuilder.setSound(UtilSound.getSoundUri(not)); //int

Это мой UtilSound.getSoundUri(int)

public static Uri getSoundUri(int not) {
        switch (not) {
            case 0:
                return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
            default:
                try {
                    File file=new File(Environment.getExternalStorageDirectory()+"/Animeflv/cache/.sounds",getSoundsFileName(not));
                    if (file.exists()) {
                        file.setReadable(true,false);
                        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
                            return FileProvider.getUriForFile(context, "knf.animeflv.RequestsBackground",file);
                        }else {
                            return Uri.fromFile(file);
                        }
                    }else {
                        Log.d("Sound Uri","Not found");
                        return getSoundUri(0);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    return getSoundUri(0);
                }
        }
    }

В AndroidManifest.xml:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="knf.animeflv.RequestsBackground"
    android:exported="false"
    android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
</provider>

провайдер_пути.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="_.sounds" path="Animeflv/cache/.sounds/"/>
</paths>

person Jordy Mendoza    schedule 07.09.2016    source источник
comment
stackoverflow.com/questions/39285228/   -  person CommonsWare    schedule 07.09.2016


Ответы (1)


Следующее взято из сообщения в блоге. что я только что опубликовал, воспроизвел здесь, потому что, эй, почему бы и нет?


Вы можете поместить собственный рингтон на Notification с помощью таких методов, как setSound() на NotificationCompat.Builder. Для этого требуется Uri, и это вызывает проблемы на Android 7.0, как сообщают несколько человек на Переполнение стека.

Если вы использовали значения file: Uri, они больше не работают на Android 7.0, если ваш targetSdkVersion 24 или выше, так как звук Uri проверяется на соответствие запрет на file: Uri значений.

Однако, если вы попробуете content: Uri, скажем, из FileProvider, ваш звук не будет воспроизводиться... потому что у Android нет доступа для чтения к этому контенту.

Вот несколько вариантов решения этой проблемы.

Скальпель: grantUriPermissions()

Вы всегда можете предоставить разрешения для контента другим приложениям с помощью grantUriPermissions(), метода, доступного на Context. Задача состоит в том, чтобы узнать, кому предоставлять разрешения.

Что работает на Nexus 6P (Android 6.0... все еще...) и Nexus 9 (Android 7.0):

grantUriPermission("com.android.systemui", sound,
    Intent.FLAG_GRANT_READ_URI_PERMISSION);

(где sound — это Uri, который вы используете с setSound())

Сохранится ли это для всех устройств и всех версий ОС Android, я не могу сказать.

Гильотина: никаких пользовательских файлов

android.resource как схема отлично работает для Uri значения для setSound(). Вместо того, чтобы позволять пользователям выбирать свою собственную мелодию звонка из файла, вы разрешаете им выбирать только одну из нескольких мелодий звонка, которые вы отправляете в качестве необработанных ресурсов в своем приложении. Однако, если это означает потерю функциональности приложения, ваши пользователи могут быть не впечатлены.

Топор: используйте собственный ContentProvider

FileProvider нельзя использовать при экспорте, происходит сбой при запуске. Однако в этом случае единственным content: Uri, который будет работать без других проблем, является тот, где провайдером является exported и у него нет разрешений на чтение (или требуется какое-то разрешение, которое com.android.systemui или его эквивалент имеет).

В конце концов, я добавлю параметры для этого в мои StreamProvider как часть некоторых "только для чтения" функционал провайдера.

Но для этого вы можете свернуть своего собственного провайдера.

Бензопила: Запрет запрета

Следующий фрагмент кода блокирует все проверки StrictMode, связанные с поведением виртуальной машины (т. е. все, что не связано с поведением основного потока приложения), включая запрет на значения file: Uri:

StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().build());

В качестве альтернативы вы можете настроить свой собственный VmPolicy с любыми правилами, которые вы хотите, просто без вызова detectFileUriExposure().

Это позволяет вам использовать значения file: Uri где угодно. Есть веские причины, по которым Google запрещает file: Uri, и поэтому попытка избежать запрета может в долгосрочной перспективе укусить вас за несчастные части тела.

Nuke: используйте более низкий targetSdkVersion

Это также снимает запрет на file: Uri значения, а также все другие действия, на которые соглашается targetSdkVersion от 24+. Следует отметить, что это приведет к тому, что ваше приложение будет отображать «может не работать с разделенным экраном» Toast, если пользователь входит в многооконный режим разделенного экрана.

Настоящее решение: исправление в Android

NotificationManager должен вызывать для нас grantUriPermissions(), или у нас должен быть какой-то другой способ связать FLAG_GRANT_READ_URI_PERMISSION с Uri, который мы используем для пользовательских звуков Notification. Следите за новостями.

person CommonsWare    schedule 07.09.2016
comment
Спасибо, Скальпель был ответом!! - person Jordy Mendoza; 07.09.2016
comment
Большое спасибо за исчерпывающий ответ. Чтобы проверить надежность решения grantUriPermission («Скальпель»), я протестировал его на различных физических устройствах (включая LG, Motorola, HTC, Samsung и Sony) с API 15 до API 24. Оно работает корректно на всех устройствах. из них, поэтому решение кажется довольно надежным (хотя, безусловно, хакерским). - person jmart; 07.09.2016
comment
@jmart: хотя, конечно, хакерский - абсолютно. Использование ContentProvider только для чтения (см. The Axe) — лучший ответ. Я, вероятно, настрою свой StreamProvider так, чтобы, если вы пометите его как экспортированный, он рассматривал все как доступный только для чтения, что довольно чисто решило бы эту проблему. - person CommonsWare; 07.09.2016
comment
@CommonsWare, где именно вы вызываете GrantUriPermission? Вам нужно делать это каждый раз при создании уведомлений, или будет достаточно предоставлять разрешения всякий раз, когда выбор uri менялся? - person Allan W; 15.11.2017
comment
@AllanW: Насколько я знаю, для этого сценария вам нужно будет вызывать только grantUriPermissions() для каждого отдельного значения Uri. Итак, в вашем случае, когда выбор изменился. При этом я сомневаюсь, что есть какой-то вред в том, чтобы каждый раз просто звонить grantUriPermissions(). - person CommonsWare; 15.11.2017
comment
@CommonsWare спасибо. Есть ли у вас также какие-либо идеи, почему не вызов этого, кажется, работает для некоторых устройств? Например, у меня есть OP3 на Nougat (на базе Oxygen), и он отлично работает. То же самое касается моего Nexus 5 на кастомной прошивке с нугой. Я получил несколько отчетов от Crashlytics, и, как вы упомянули, об этой ошибке, похоже, сообщили лишь несколько человек. - person Allan W; 15.11.2017
comment
@AllanW: Без понятия, извини. - person CommonsWare; 15.11.2017
comment
Похоже, это должно быть исправлено в Android P: github.com/aosp-mirror/platform_frameworks_base/ зафиксировать/ - person rcell; 30.10.2018
comment
@CommonsWare, могу ли я установить звук уведомления из файла расширения apk? - person Pranav; 16.05.2019
comment
@Pranav: я не знаю, извините. - person CommonsWare; 16.05.2019
comment
Разве StrictMode не будет отключен в производственных сборках? Я удивлен, что он запускается кодом Google даже в битах выпуска. - person fobbymaster; 10.09.2019
comment
@fobbymaster: Разве StrictMode не будет отключен в производственных сборках? - хотя вы можете это сделать, это не поможет с Android 10. У вас может не быть доступа к файлу, а код, использующий Uri, может не иметь доступа к файлу. - person CommonsWare; 10.09.2019
comment
Извините, позвольте мне перефразировать: почему StrictMode вообще работает в производственных сборках без отключения? Почему мы должны отключить его вообще? Разве он не должен быть уже отключен? - person fobbymaster; 10.09.2019
comment
@fobbymaster: я не могу ответить на этот вопрос, извините. - person CommonsWare; 11.09.2019
comment
@CommonsWare Просто обновление по этому поводу, оно наконец-то исправлено в сентябре прошлого года. - person Shadow; 09.02.2021