Введение
SDK android дает богатые возможности для работы с телефоном. Есть возможность получать фотографии, отслеживать перемещения телефона, записывать звук с микрофона, перехватывать sms и т.д. Перечисленные возможности попадают под категорию конфиденциальные. К сожалению, до недавнего времени, единственный способ узнать какие разрешения будет использовать приложение, можно было увидеть только в диалоговом окне при установке приложения. На практике, данный подход оказался неэффективен, т.к. пользователь обычно не читает и автоматически соглашается с установкой. В результате в google play появилась куча приложений типа “Фонарик”, которые получали доступ к разрешениям которые совершенно не нужных для выполнения своих задач.
С выходом android 6 (API level 23) ситуация изменилась. Появились разрешения, которые необходимо принимать при работе приложения. Работает это точно так же как и в iOS. При запуске приложения появляется системный диалог, который и предлагает пользователю разрешить или запретить доступ приложения к разрешению.
Виды Permissions
Теперь разрешения разделены на два типа:
- обычные (normal);
- опасные (dangerous
Обычные разрешения - это группа разрешений, которые не связаны с приватными данными. Они описываться в manifest.xml и принимаются пользователем автоматически при установки приложения. Опасные разрешения - дают доступ к приватным данным пользователя.
Список опасных разрешений:
READ_CALENDAR
WRITE_CALENDAR
CAMERA
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
ACCESSFINELOCATION
ACCESSCOARSELOCATION
RECORD_AUDIO
READPHONESTATE
CALL_PHONE
READCALLLOG
WRITECALLLOG
ADD_VOICEMAIL
USE_SIP
PROCESSOUTGOINGCALLS
BODY_SENSORS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVEWAPPUSH
RECEIVE_MMS
READEXTERNALSTORAGE
WRITEEXTERNALSTORAGE
С версии android API level 23, необходимо обязательно запрашивать разрешение у пользователя, иначе приложение упадет с исключением:
Также у пользователя появилась возможность в любой момент можно дать или запретить разрешение. Для этого в настройках приложения появился новый пункт меню “Разрешения”.
Когда следует запрашивать разрешения
Базовые разрешения, без которых приложение не сможет запуститься следует запрашивать при старте приложения. Остальные разрешения следует запрашивать только перед непосредственным использованием.
Постановка задачи
Создадим простое приложение, которое по нажатию на кнопку будет выводить список имён из адресной книги телефона.
Создание проекта
Создадим новый проект с Empty Activity. В activity_main.xml создадим одну кнопку и ListView.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_permission_read_contacts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_read_contacts"/>
<ListView
android:id="@+id/list_contacts"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Добавим строковый ресурсы в strings.xml:
<string name="text_read_contacts">Read contacts</string>
А также добавим разрешение в manifest.xml:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Инициализируем view компоненты и повесим обработчик нажатия на кнопку.
private static final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 1234;
private ListView mListContacts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonReadContacts = (Button) findViewById(R.id.btn_permission_read_contacts);
mListContacts = (ListView) findViewById(R.id.list_contacts);
buttonReadContacts.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getContacts();
}
});
}
public void getContacts(){
//Проверяем есть ли у нас разрешение на чтение контактов
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS)) {
//Здесь выводим сообщение в котором поясняем почему необходимы разрешение
Toast.makeText(this,"Need for show contacts. Please turn on permission at [Setting]>[Permission]", Toast.LENGTH_SHORT).show();
} else {
//Запрашиваем разрешение
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
}
return;
}
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if(phones == null){
return;
}
List<String> nameList = new ArrayList<>();
while (phones.moveToNext()) {
String user = "";
user = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
nameList.add(user);
}
phones.close();
ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1
, android.R.id.text1, nameList);
mListContacts.setAdapter(adapter);
}
Обработать результат запроса разрешения следует в onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults), где requestCode есть id который был передан в ActivityCompat.requestPermissions().
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Здесь мы получаем разрешение и снова вызываем getContacts()
getContacts();
} else {
Toast.makeText(this, "Read contacts permission is denied.", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
Заключение
Android Runtime Permissions в первую очередь направленно на улучшение безопасности персональных данных и привлечения внимания пользователя к разрешениям, которые использует приложение.