Приложению контакты было отказано в доступе к функции хранения android permission
Безопасность данных в операционной системе является очень важной задачей, и Android здесь не является исключением. Так, Google в Android 10 добавили новый способ обеспечения безопасности, называемый хранилищем с ограниченной областью видимости (Scoped storage).
До Android 10 всё работало достаточно просто: приложение запрашивало доступ к хранилищу, используя одно из разрешений (WRITE_EXTERNAL_STORAGE либо READ_EXTERNAL_STORAGE), и, после того как пользователь предоставлял разрешение, приложение получало возможность прочесть и изменить практически любой файл, хранящийся на устройстве, за исключением системных файлов и папок, а также папок других приложений. Иначе говоря, приложение просто получало доступ ко всей файловой системе. Scoped storage же изменил этот подход. Теперь приложение по умолчанию имеет доступ только к некоторым участкам памяти, где хранятся общедоступные файлы, такие как медиа, загруженные файлы, некоторые документы. При этом полный доступ приложение имеет только к тем файлам, которые находятся непосредственно в папке приложения, расположенной в Android/data/ . Если приложению нужно изменить или удалить файл, находящийся вне этой папки, то приложение должно запросить у пользователя разрешение на конкретную операцию с помощью MediaStore API (для медиа-файлов) или через Storage Access Framework (для всех остальных файлов). Однако этот способ очень неудобен, если мы разрабатываем файловый менеджер или приложение, которое должно работать не с медиа-файлами.
Поскольку часть приложений всё же требует для своей работы полный доступ к хранилищу, а не ограниченный, Google добавили новое разрешение MANAGE_EXTERNAL_STORAGE для таких случаев. Это разрешение, как и раньше, позволяет получить доступ ко всей файловой системе, однако для его использования в приложении требуется подтверждение со стороны Google. В этой статье мы разберём, как использовать разрешение в приложении, а также рассмотрим процесс заполнения заявки в консоли Google Play.
Для начала создадим новый проект с пустой активностью. Для начала нам нужно определить в манифесте, что приложению нужны следующие разрешения для работы. Добавим их в файле AndroidManifest.xml.
Поскольку MANAGE_EXTERNAL_STORAGE используется в обязательном порядке только начиная с API 30, то нам всё ещё нужно обрабатывать запрос разрешений на предыдущих версиях так, как мы это делали раньше. Для этого мы добавляем также WRITE_EXTERNAL_STORAGE, но ограничиваем его использование API 29. Таким образом, мы будем каждый раз проверять уровень API устройства и, в зависимости от этого, использовать либо старое разрешение, либо новое.
Как уже упоминалось выше, в случае с Android 10 для работы с файловой системой нам также нужно в манифесте добавить следующий флаг в элемент <application>.
В файле разметки activity_main.xml добавим несколько элементов для тестирования:
Перейдём к написанию кода. Создадим класс PermissionUtils, в котором будет находиться проверка разрешений и их запрос.
Затем в классе активности MainActivity.java определим элементы из разметки.
Текстовое поле будет отображать, если у приложения необходимые разрешения. Для этого после объявления переменных добавим проверку.
Добавим обработчик для кнопки, который будет отправлять запрос на предоставление разрешения.
Нам также необходимо задать код запроса, чтобы мы могли после определить, что ответ пришёл именно для этого запроса.
Когда пользователь нажмёт на кнопку, у него откроется новая активность с настройками приложения, в которой будет предложено дать полный доступ данному приложению. Пользователь может как согласиться, так и отклонить запрос.
Поскольку это также работает и для старых версий Android, то в случае с ними пользователю вместо активности будет предложен старый диалог с запросом на предоставление разрешений.
Любой результат действий пользователя в итоге возвращается в нашу активность. Поскольку для API 30 мы запускаем отдельную активность, то результат работы мы должны отслеживать в методе onActivityResult(). Переопределим его и добавим следующий код.
Здесь мы снова проверяем, дал ли пользователь разрешение или нет, и обновляем текстовое поле в соответствии с результатом.
Аналогичным образом проверяем результат операции для старых уровней API, но здесь уже переопределяем метод onRequestPermissionResult().
Теперь у нашего приложения есть полный доступ к файловой системе, и технически этого уже достаточно. Однако если попытаться опубликовать такое приложение в Google Play, то публикацию запретят по причине отсутствия заявки на получение доступа ко всем файлам. Поэтому, после того, как в консоли будет создан выпуск и загружена туда новая версия приложения, нам нужно перейти в раздел Контент приложения и выбрать там появившийся пункт Важные разрешения и API.
В появившейся форме требуется описать подробно, зачем приложению требуется доступ ко всем файлам, относится ли это к основному функционалу приложения, а также объяснить с технической точки зрения почему невозможно использовать в приложении альтернативные способы.
Также, в случае если показать работу приложения проще, чем объяснить, можно также записать демо-ролик работы приложения, загрузить его на Youtube и прикрепить в данной форме ссылку на видео. В некоторых случаях это может ускорить получение доступа.
Когда Google одобрил доступ для приложения, новая версия успешно публикуется, а в разделе Контент приложения указано разрешение, которое было одобрено.
Однако, Google также могут и не одобрить заявку, если посчитают, что это не является обязательной функцией или если можно ограничиться MediaStore\Storage Access Framework. Здесь может помочь изменение в работе приложения либо переоформление заявки, это нужно учитывать.
Таким образом, с помощью пары строк кода можно обновить своё приложения для работы с файловой системой на новых версиях Android.
Одним из интереснейших методов безопасности операционной системы Андроид является система разрешений (permissions), используемых приложениями. Когда OS ANDROID только появилась, её разработчики придумали – выделить все возможные функции, доступ к которым необходим приложению, и позволить пользователю их контролировать. Было это реализовано довольно интересно. Список возможных разрешений создан разработчиками Google и зафиксирован в документации. Он очень гибкий, в нем есть всё, что нужно для обеспечения какого угодно сложного функционала. Вместе с тем он грамотно разграничен.
С технической точки зрения, обойти существующий механизм прав доступа приложений к функциональности системы Андроид очень непросто. Так как менеджмент разрешений осуществляется на самом низком уровне ядром Linux, программе обязательно нужны права рут , чтобы повлиять на это. Хорошо формализованная система permissions облегчает реализацию инструментов безопасности сторонними разработчиками. Перспективным направлением является создание программ, которые позволяют пользователям тонко настраивать права доступа каждого отдельного приложения, предотвращая любые утечки информации с устройства.
Права доступа (permission) на папки и файлы в Андроид
Права доступа разделяются на две группы зто:
1.Права доступа к файлам
2.Права доступа к папке (директории)
Права доступа к файлам могут иметь такие атрибуты:
r — право на чтение данных. (read)
w — право на изменение содержимого или запись, но не удаление. (write)
x — право на исполнение файла. (xxxxxx)
Права доступа к папке (директории):
r — право на чтение папки (директории).
w — право на изменение содержимого директории можно создавать и удалять объекты в этой директории.
x — право, которое позволяет вам войти в директорию.
Сами права доступа подразделяются на три категории:
«user» — u владелец файла.
«group» — g член той же группы, к которой принадлежит владелец.
«world» — o все остальные.
Порядок записи прав доступа:
сначала права для владельца — «u»
затем для группы — «g»
и в конце права для всех остальных — «o»
Например: предположим что у вас на работе есть компьютер, вы его владелец, он состоит в локальной сети (группа) а есть пользователи, которые хотят что либо на вашем компьютере сделать. По всем этим категориям задаются права на файлы и папки в виде RWX которые дают какие либо права на выполнение чего либо, если в заданных правах RWX присутствует знак «-» то это означает что право отсутствует.
Например: rwx r-- r-- означает что владелец файла имеет все права: право на чтение, запись в него и исполнение, а все остальные пользователи только право на чтение.
Помимо буквенных выражений есть числовые выражения:
r - (читать) это 4
w (запись) это 2
x (исполнение) это 1
«-» ничего не делать тоесть знак дефиса, 0
И их сумма означает конечные права
7 (rwx) = 4 + 2 +1 (полные права)
5 (r-x)= 4 + 0 + 1 (чтение и выполнение)
6 (rw-) = 4 + 2 + 0 (чтение и запись)
4 (r--) =4 + 0 + 0 (только чтение)
Часто используемые параметры:
400 (-r--------) - владелец будет иметь право чтения, никто кроме него не имеет права выполнять никакие действия.
644 (-rw-r--r--) - все пользователи имеют право чтения, а владелец может редактировать.
660 (-rw-rw----) - владелец и группа могут читать и редактировать, все остальные не имеют никаких прав.
664 (-rw-rw-r--) - все пользователи имеют право чтения, а владелец и группа могут редактировать.
666 (-rw-rw-rw-) - все пользователи могут читать и редактировать.
700 (-rwx------) - владелец может читать, записывать и запускать на выполнение, у других нет права выполнять никакие действия.
744 (-rwxr--r--) - все пользователи могут читать, а владелец имеет право редактировать и запускать на выполнение.
755 (-rwxr-xr-x) - каждый пользователь может читать и запускать на выполнение, владелец может редактировать.
777 (-rwxrwxrwx) - каждый пользователь может читать, редактировать и запускать на выполнение.
sudo passwd root - пароль суперпользователя root.
Здесь представлен онлайн калькулятор , и программа которая может задавать права на файл Root Explorer
Бывает что права состоят из 4х цифр это означает что помимо владельца, группы, остальных есть еще и SUPERUser (Супер Админ)
тогда список будет выглядеть вот так:
«SuperUser» — SuperUser
«user» — u владелец файла
«group» — g член той же группы, к которой принадлежит владелец
«world» — o все остальные
Часто при установке приложения на Android нам приходилось видеть, что оно запрашивает какое-то немыслимое количество разрешений. Например:
Хорошо, если вы устанавливаете приложение от какого-то известного разработчика, которому можете доверять. Но весьма подозрительно, если вы устанавливаете новый музыкальный плеер, а ему для работы требуется, например, получать ваше местоположение. Или, тем более, фонарик, требующий доступ к смс и звонкам.
Некоторые разработчики, чтобы уменьшить недоверие, добавляют в описание приложения на Google Play информацию о том, зачем нужно то или иное разрешение.
К шестой версии Android ситуация поменялась. Теперь разрешения нужно запрашивать в процессе работы. О том, как этой новой возможностью пользоваться и ее некоторых подводных камнях будет рассказано далее.
Общая информация
Разрешения делятся на два типа (есть и другие, но они нас не интересуют):
Можно увидеть, что доступ к интернету не считается опасным. Все, кто использует рекламу в своих программах, могут вздохнуть с облегчением: отключить её, просто отобрав разрешение, не получится (все еще можно просто отключить интернет, но факт остается фактом).
Взаимодействие с пользователем
Посмотрим, как нужно производить взаимодействие с пользователем. Начнем непосредственно с запроса разрешения. С обычными разрешениями все понятно, это дело установщика приложений, нас это не интересует, а то, как мы будем запрашивать опасные разрешения, зависит от двух вещей.
Первая из них — это важность данного разрешения для вашего приложения, от этого зависит, когда нужно производить запрос. Если функция критична и без нее программа не будет иметь смысла, то смело просите разрешения прямо при запуске приложения. Например, если вы разрабатываете приложение для обмена sms, то без соответствующих разрешений с ним ничего не получится сделать, оно теряет всякий смысл. А если пользователь отказал, то не пропускаем его дальше, но даем возможность снова вызвать диалог запроса и даем инструкции что нужно делать.
- важные разрешения запрашиваем при запуске, вторичные — при первом использовании соответствующей функции;
- если понять, зачем нужно разрешение тяжело, предоставляем объяснение.
Но даже если у вас все хорошо с проверками и нет возможности заниматься внедрением новых возможностей, стоит перепроверить, все ли правильно работает, потому что иногда можно получить null не там, где его ожидаешь. Так, например, при использовании Environment.getExternalStorageDirectory() без наличия разрешения из группы Storage, мы получим File, но list() вернет нам заветный null. В документации такой исход описан, но для ситуации, когда File не является директорией. Так что проверка в любом случае лишней не будет.
Есть возможность добавить разрешение только для Android M и выше. Для это в манифесте нужно использовать новый тег <uses-permission-sdk-23 /> (ранее называвшийся <uses-permission-sdk-m />). Его синтаксис аналогичен обычному <uses-permission />. Это может быть полезно, если вы хотите добавить в существующее приложение возможность, которая требует дополнительного разрешения. Как вы помните, если новая версия программы требует дополнительных прав, пользователь должен вручную подтвердить ее обновление. Этого можно избежать, если новая функция не очень важна, отключив ее для более ранних версий системы, используя описанный ранее тег. В таком случае это разрешение будет вовсе отсутствовать.
В процессе отладки часто приходится включать/отключать разрешения. Заходить для этого каждый раз в настройки приложения не очень удобно. К счастью, это можно сделать с помощью adb:
И еще несколько полезных команд, смысл которых ясен из названия:
Перейдем к непосредственной реализации (предварительно не забудем обновить compileSdkVersion и targetSdkVersion до версии 23).
До момента, когда Marshmallow станет минимальной версией андроида для ваших приложений, еще далеко, поэтому нужно позаботиться об обратной совместимости. Конечно, можно делать проверки версии sdk, но зачем, если все реализовано за нас в support library v4 (ActivityCompat) и v13 (FragmentCompat). Если все же вам понадобятся оригинальные методы, то найти их не составит труда.
Во всех примерах используется ActivityCompat, так как они были сделаны для activity. Для fragment нужно использовать FragmentCompat. Если вы по какой-то причине не используете activity и fragment из support библиотек, то вам нужно реализовать интерфейс ActivityCompat.OnRequestPermissionsResultCallback или FragmentCompat.OnRequestPermissionsResultCallback соответственно.
Далее, если разрешение есть, выполняем нужное нам действие, а если нет, то его нужно запросить. Одновременно можно запросить несколько разрешений (пользователю по очереди будет показан запрос на каждое из них), если это необходимо.
Стоит упомянуть, что если разрешения находятся в одной permission group, то запросить достаточно одно из них, так как все остальные элементы этой группы станут также доступны. Но так делать не нужно. Потому что в будущем состав групп может поменяться, поэтому при запросе разрешений не нужно делать предположений относительно того находятся ли они в одной группе или нет.
UPD будто в подтверждение предыдущего параграфа, начиная с Android 8.0 разрешения из одной permission group не выдаются сразу — каждое разрешение нужно запрашивать отдельно, но все разрешения из одной группы будут выданы автоматически, без участия пользователя при первом же их запросе.
UPD2 это же поведение было замечено на Android 7.0 — если часть разрешений из группы выдана (не могу сказать с уверенностью, имеет ли значение какие именно), то остальные будут выдаваться по запросу сразу же без показа диалога. Это может вызвать проблемы, если ваше приложение объясняет пользователю зачем ей нужно то или иное разрешение еще до его запроса. В реальной жизни такое редко когда может возникнуть (только при использовании adb комманд), но стоит учитывать это при отладке приложения.
Для запроса используется метод ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode). Массив permissions соответственно содержит названия разрешений, которые вы хотите запросить. Отсюда видно, что одновременно можно запрашивать несколько разрешений. requestCode — значение, по которому в дальнейшем можно будет определить, на какой запрос разрешения вам пришел ответ подобно тому как мы получаем результат от activity, используя startActivityForResult. Кстати, если посмотреть на код requestPermission, то обнаружится, что это всего лишь особая версия startActivityForResult.
Как видите, напрямую запрашивать разрешения можно только из Activity или Fragment. Если разрешение требуется сервису, то придется запускать Activity, из которой уже можно будет сделать запрос. Лучше всего перед этим будет показать уведомление, содержащее информацию о недостающем разрешении с кнопкой для запуска этой самой Activity.
Результат запроса разрешения следует обрабатывать в onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults). Параметры requestCode и permissions содержат данные, которые вы передавали при запросе разрешений. Основные данные здесь несет массив grantResults, в котором находится информация о том, получены разрешения или нет. Каждому i-му элементу permissions соответствует i-ый элемент из grantResults. Их возможные значения аналогичны результату checkSelfPermission.
Размер массива grantResults проверяется для того, чтобы удостовериться, что запрос разрешения не был прерван (в этом случае permissions и grantResults не будут содержать элементов). Такую ситуацию следует рассматривать не как запрет разрешения, а как отмену запроса на него.
Если вы ранее уже запрашивали разрешение, но пользователь отказался предоставить его, необходимо объяснить ему причину запроса. Этого не нужно делать, если причина, по которой вы запрашиваете разрешение, абсолютно ясна. Если же есть вероятность, что вопрос “А зачем приложению это нужно?” возникнет, то объяснить это крайне желательно. Для того чтобы узнать, нужно ли показывать объяснение есть метод shouldShowRequestPermissionRationale(@NonNull Activity activity, @NonNull String permission), который возвращает boolean. Само же объяснение можно реализовать, например, с помощью Snackbar с кнопкой действия, по клику на которой происходит запрос разрешения, или диалогового окна, если разрешение критично необходимо.
Never ask again
Одной из проблем может стать опция “Never ask again”, которая появляется при повторном запросе разрешения, после того как пользователь уже отказал ранее. Как видно из названия, при её выборе диалог запроса не будет больше появляться. shouldShowRequestPermissionRationale будет выдавать false, а в onRequestPermissionsResult будет получен результат PackageManager.PERMISSION_DENIED. И получим разрешение мы, только если включить его непосредственно через настройки приложения в разделе Permissions.
Что с этим можно сделать? В первую очередь, конечно, сообщить пользователю, что для выполнения действия нет нужных прав. Далее возможным действием может быть предложение перейти в настройки и предоставить это разрешение вручную. Не лучший вариант, но лучше чем ничего. Реализовать это можно вновь с использованием Snackbar с кнопкой действия.
Перейти непосредственно на страницу с разрешениями не получится, поэтому лучшее, что вы можете сделать, это открыть настройки своего приложения. После этого можно, например, показать Toast с информацией, что нужно сделать.
В примере используются startActivityForResult и onActivityResult чтобы определить, что пользователь вернулся из activity настроек обратно в приложение и попробовать выполнить действие, которое нельзя было сделать без нужного разрешения. В методе showExtDirFilesCount нужно снова проверить есть ли разрешение для уверенности, что пользователь его все-таки выдал.
Здесь может возникнуть ситуация, которая не особенно мешает, если вы используете Snackbar для показа rationale, но портит UX, если вы решили использовать диалоги (причины этого решения мы не затрагиваем). А именно двойное появление rationale, до запроса разрешения и после него. Как это может произойти? У нас всего два метода, по которым мы можем судить о состоянии разрешения. Проблема в том, что до запроса разрешения ситуация, когда мы еще никогда не запрашивали это разрешение, и ситуация, когда пользователь ранее выбрал “Never ask again”, абсолютно одинаковы по значениям. А именно checkSeflPermission возвращает нам PERMISSION_DENIED, a shouldShowRequestPermissionRationale — false. Значит, показывать диалог для открытия настроек мы будем в onRequestPermissionsResult, где значение shouldShowRequestPermissionRationale точно будет разным для этих двух ситуаций. Все отлично? Не совсем. В этом callback’e никак нельзя определить была ли показана rationale или нет. Поэтому если вы показываете причину запроса, а далее пользователь просит больше его не спрашивать об этом разрешении, после нажатия на кнопку DENY он получит очередной rationale диалог, приглашающий его в настройки программы. Хорошие программы так себя не ведут.
Что делать в такой ситуации? В сети есть пара не очень красивых решений: одно из них — сохранять в SharedPreferences информацию о том имеется ли разрешение или нет, другое — хранить флаг о том была показана rationale или нет внутри класса. Первое решение не хорошо тем, что пока приложение не работает, пользователь может изменить настройки разрешений и информация в preferences будет неактуальной. Второй же способ не особо красивый.
Хорошим вариантом (на мой взгляд) будет завести два requestCode для каждого запроса, один для использования в rationale другой в остальных случаях. Этот способ так же не идеален и не особенно красив, но помогает придерживаться существующих методов, не внося ничего нового.
Intent
Есть еще одна важная рекомендация при использовании runtime разрешений. Не используйте их. Точнее, используйте, но только тогда, когда функционал, который вы собираетесь реализовать с их помощью, не сделал уже кто-то до вас.
В качестве самого показательного примера чаще всего вспоминают камеру. Используйте стандартное приложение камеры (или другие приложения, умеющие это делать), если вам нужно всего лишь сделать фотографию без какой-то особой логики. В этом вам помогут Intent’ы (подробнее).
Таким образом, вы сможете избавиться от некоторых опасных разрешений и упростите работу с приложением.
Операционная система Android устроена таким образом, что для выполнения некоторых операций или доступа к определенным ресурсам, приложение должно иметь разрешение на это.
Разрешения могут быть двух типов: normal и dangerous. Отличие между ними в том, что dangerous разрешения опасны, т.к. могут быть использованы для получения ваших личных данных или информации о вас, или еще каким-то способом могут навредить вам. Примеры dangerous разрешений - это доступ к контактам или смс.
Полный список существующих разрешений можно посмотреть здесь. Характеристика Protection level подскажет насколько опасно это разрешение. А здесь можно сразу просмотреть весь список normal разрешений.
Если приложению необходимо получить какое-либо разрешение, то оно должно быть указано в AndroidManifest.xml, в корневом теге <manifest>. Тег разрешения - <uses-permission>.
Вот пример манифеста с разрешениями:
Здесь мы указываем, что приложению понадобятся разрешения на работу с интернет, контактами, bluetooth, локацией, камерой и смс. Пользователю необходимо будет подтвердить, что он предоставляет приложению эти разрешения.
В этом материале мы подробно рассмотрим, как происходит это подтверждение.
До Android 6
До выхода Android 6 все было просто и легко. Когда пользователь устанавливал приложение с манифестом, который мы рассмотрели чуть выше, то он видел такой экран:
Система показывает разрешения, которые были прописаны в манифесте. Сначала те, которые могут быть опасными с точки зрения приватности (отправка смс, доступ к камере/местоположению/контактам), а затем - обычные (интернет, bluetooth).
Таким образом пользователь видит, на что претендует приложение, и может примерно понять все ли в порядке. Если, например, приложение калькулятор при установке просит у вас доступ к контактам и смс, то скорее всего, что-то не так с этим приложением и оно может быть опасным для ваших данных.
Нажав кнопку Install, пользователь автоматически подтверждает свое согласие, что приложению будут предоставлены эти запрашиваемые разрешения. И далее, когда приложение, например, пытается в коде получить список контактов, то оно без проблем их получает.
Если же в манифесте не указать разрешение READ_CONTACTS, то его не будет и в списке тех разрешений, которые подтверждает пользователь. Соответственно, система не предоставит этому приложению доступ к контактам. И при попытке получить список контактов, будет ошибка:
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2
Android 6
С выходом Android 6 механизм подтверждения поменялся. Теперь при установке приложения пользователь больше не видит списка запрашиваемых разрешений. Приложение автоматически получает все требуемые normal разрешения, а dangerous разрешения необходимо будет программно запрашивать в процессе работы приложения.
Т.е. теперь недостаточно просто указать в манифесте, что вам нужен, например, доступ к контактам. Когда вы в коде попытаетесь запросить список контактов, то получите ошибку SecurityException: Permission Denial. Потому что вы явно не запрашивали это разрешение, и пользователь его не подтверждал.
Перед выполнением операции, требующей разрешения, необходимо спросить у системы, есть ли у приложения разрешение на это. Т.е. подтверждал ли пользователь, что он дает приложению это разрешение. Если разрешение уже есть, то выполняем операцию. Если нет, то запрашиваем это разрешение у пользователя.
Давайте посмотрим, как это выглядит на практике.
Проверка текущего статуса разрешения выполняется методом checkSelfPermission
На вход метод требует Context и название разрешения. Он вернет константу PackageManager.PERMISSION_GRANTED (если разрешение есть) или PackageManager.PERMISSION_DENIED (если разрешения нет).
Если разрешение есть, значит мы ранее его уже запрашивали, и пользователь подтвердил его. Можем получать список контактов, система даст нам доступ.
Если разрешения нет, то нам надо его запросить. Это выполняется методом requestPermissions. Схема его работы похожа на метод startActivityForResult. Мы вызываем метод, передаем ему данные и request code, а ответ потом получаем в определенном onResult методе.
Добавим запрос разрешения к уже имеющейся проверке.
Проверяем разрешение READ_CONTACTS. Если оно есть, то читаем контакты. Иначе запрашиваем разрешение READ_CONTACTS методом requestPermissions. На вход метод требует Activity, список требуемых разрешений, и request code. Обратите внимание, что для разрешений используется массив. Т.е. вы можете запросить сразу несколько разрешений.
После вызова метода requestPermissions система покажет следующий диалог
Здесь будет отображено разрешение, которое мы запросили методом requestPermissions. Пользователь может либо подтвердить его (ALLOW), либо отказать (DENY). Если будет запрошено сразу несколько разрешений, то на каждое из них будет показан отдельный диалог. И пользователь может какие-то разрешения подтвердить, а какие-то нет.
Решение пользователя мы получим в методе onRequestPermissionsResult
Проверяем, что requestСode тот же, что мы указывали в requestPermissions. В массиве permissions придут название разрешений, которые мы запрашивали. В массиве grantResults придут ответы пользователя на запросы разрешений.
В итоге схема получения разрешения состоит из трех действий:
- проверка текущего состояния разрешения
- запрос на получение разрешения, если оно еще не было получено
- обработка ответа на запрос
Далее поговорим про некоторые дополнительные возможности, нюансы и прочие мелочи.
Манифест
При использовании новой схемы разрешений вам все равно необходимо указывать разрешение в манифесте. Если его там не указать и сделать запрос на это разрешение, то вам просто сразу придет отказ без всякого диалога.
Всегда проверяйте разрешение
Каждый раз (а не только первый) перед выполнением операции, требующей определенного разрешения, необходимо проверять, что это разрешение есть. Потому что, даже если пользователь уже давал это разрешение, он всегда может зайти в настройки приложения и отменить его. И если вы после этого не выполните проверку, то получите ошибку при выполнении операции.
Don’t ask again
Когда вы первый раз делаете запрос на какое-либо разрешение, пользователь может отказать. При последующих запросах этого же разрешения, в диалоге появится чекбокс Don’t ask again
Если пользователь включит этот чекбокс, то при последующих ваших запросах диалог не будет отображаться, а в onRequestPermissionsResult сразу будет приходить отказ.
Объяснение для пользователя
Когда вы запрашиваете разрешение, пользователю должно быть очевидно, зачем приложению понадобилось это разрешение, и у него не должно возникать вопросов. Но случаи бывают разные, и вы можете решить, что вам надо явно объяснить пользователю, почему приложению понадобилось это разрешение.
Диалог, который показывается при запросе разрешения, - системный, вы не можете менять его содержимое и добавлять туда свой текст. Но вы можете сделать свой диалог или что-то подобное и показать его перед тем, как будете делать запрос разрешения.
Есть метод shouldShowRequestPermissionRationale, который может быть полезен в данной ситуации. Передаете ему название разрешения, а он вам в виде boolean ответит, надо ли показывать объяснение для пользователя.
Т.е. вы сначала проверяете наличие разрешения. Если его нет, то вызываете shouldShowRequestPermissionRationale, чтобы решить, надо ли показывать объяснение пользователю. Если не надо, то делаете запрос разрешения. А если надо, то показываете ваш диалог с объяснением, а после этого диалога делаете запрос разрешения.
Алгоритм работы метода shouldShowRequestPermissionRationale прост.
Если вы еще ни разу не запрашивали это разрешение, то он вернет false. Т.е. перед первым запросом разрешения ничего объяснять не надо.
Если вы ранее уже запрашивали это разрешение и пользователь отказал, то метод вернет true. Т.е. пользователь не понимает, почему он должен давать это разрешение, и надо ему это объяснить.
Если пользователь ставил галку Don’t ask again, то метод вернет false. Запрос полномочий все равно не будет выполнен. Объяснять что-то не имеет смысла.
Разумеется, вы можете показывать дополнительную информацию согласно вашим правилам и не использовать метод shouldShowRequestPermissionRationale.
Группы
Dangerous разрешения собраны в группы. Список групп можно посмотреть здесь. Если вы запросили одно разрешение из группы и пользователь предоставил вам его, то вы автоматически получаете все разрешения этой группы.
Например, разрешения READ_CONTACTS и WRITE_CONTACTS принадлежат группе CONTACTS. И если пользователь уже подтверждал разрешение на READ_CONTACTS, то при проверке WRITE_CONTACTS вы получите PERMISSION_GRANTED.
Android 6 и targetSdkVersion 23
Схема работы разрешений зависит от версии Android, на которой запущено приложение и от параметра targetSdkVersion приложения.
Новая схема будет работать, если версия Android >= 6 И targetSdkVersion >= 23.
В остальных случаях, т.е. когда targetSdkVersion < 23 ИЛИ версия Android < 6, разрешения будут работать по старому. Т.е. пользователь будет подтверждать их сразу все при установке. Если в приложении есть код, который проверяет разрешения, то он будет получать PERMISSION_GRANTED.
Но учитывайте, что в Android версии 6 и выше, пользователь может отменить разрешения в настройках приложения.
Intent
Не забывайте, что иногда для работы с контактами, камерой и т.п., вы можете использовать Intent и уже установленные приложения. В этом случае вам не придется писать лишний код и запрашивать разрешения для работы с этими ресурсами.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
- новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Читайте также: