Избранное сообщение

Фетісов В. С. Комп’ютерні технології в тестуванні. Навчально-методичний посібник. 2-ге видання, перероблене та доповнене / Мои публикации

В 10-х годах я принимал участие в программе Европейского Союза Tempus "Освітні вимірювання, адаптовані до стандартів ЄС". В рамк...

Благодаря Интернету количество писателей и поэтов увеличивается в геометрической прогрессии. Поголовье читателей начинает заметно отставать.

понедельник, 6 февраля 2017 г.

Работа с Runtime Permissions в Android 6. Получаем разрешения программно / Android. Разработка приложений

В этой статье мы рассмотрим работу с получением разрешений во время выполнения, которые были добавлены в Android 6 (API 23).

Runtime Permissions в Android API 23. Теория

Одним из основных изменений в Android API 23 является новая система разрешений. В более ранних версиях разрешения объявляли в файле AndroidManifest.xml и больше никаких действий не требовалось. Но с Android 6 мы должны запрашивать разрешения из категории «Опасных» во время выполнения.
В документации по работе с разрешениями указано, что их следует получать не все сразу, а по мере необходимости той или иной функции в процессе работы приложения.
Прежде чем запрашивать разрешения, нам нужно описать их в файле AndroidManifest.xml.
Процесс получения разрешений:
1). Узнать, получено ли в приложении необходимое разрешение. Например, так:

private boolean isPermissionGranted(String permission) {
        // проверяем разрешение - есть ли оно у нашего приложения
        int permissionCheck = ActivityCompat.checkSelfPermission(this, permission);
        // true - если есть, false - если нет
        return permissionCheck == PackageManager.PERMISSION_GRANTED;
    }
2). Если разрешения нет, то запросить его с помощью метода requestPermissions():

private void requestPermission(String permission, int requestCode) {
        // запрашиваем разрешение
        ActivityCompat.requestPermissions(this,
               new String[]{permission}, requestCode);
    }
Сразу несколько разрешений можно запросить так:

public void requestMultiplePermissions() {
   ActivityCompat.requestPermissions(this,
           new String[] {
                   Manifest.permission.READ_PHONE_STATE
                   Manifest.permission.READ_SMS
           },
           PERMISSION_REQUEST_CODE);
}
3). Обработать результат запроса на получение разрешений можно так:

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        // проверка по запрашиваемому коду
        if (requestCode == REQUEST_READ_PHONE_STATE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // разрешение успешно получено
            } else {
                // разрешение не получено
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
Обратите внимание на массив grantResults[] — здесь хранятся результаты по каждому из запрашиваемых разрешений. Если в методе requestPermissions()мы запрашивали 1 разрешение, то обрабатываем только 1 значение массива результатов: grantResults[0].
Некоторые разрешения перекочевали в раздел настроек: мы все также описываем их в файле манифеста, однако обрабатывать их нужно уже не внутри приложения, а на странице настроек приложения.
Давайте на примере посмотрим на разные способы получения разрешений.

Runtime Permissions в Android API 23. Практика

В нашем тестовом приложении мы будем получать данные телефона, поэтому нам нужно запрашивать разрешение на получение Manifest.permission.READ_PHONE_STATE.
В коде ниже у нас описан весь процесс обработки так называемых «Опасных» разрешений, которые требуют взаимодействия с пользователем. Для начала создайте новый проект в Android Studio.
Идем в файл AndroidManifest.xml и добавляем разрешение на работу со звонками:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Теперь идем в MainActivity.java и описываем процесс получения разрешений и обработку результатов:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.javadevblog.runtimepermissionsapp;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_READ_PHONE_STATE = 10001;

    // объявляем разрешение, которое нам нужно получить
    private static final String READ_PHONE_STATE_PERMISSION =Manifest.permission.READ_PHONE_STATE;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // проверяем разрешения: если они уже есть,
        // то приложение продолжает работу в нормальном режиме
        if (isPermissionGranted(READ_PHONE_STATE_PERMISSION)) {
            Toast.makeText(this, "Разрешения есть, можно работать",Toast.LENGTH_SHORT).show();
        } else {
            // иначе запрашиваем разрешение у пользователя
            requestPermission(READ_PHONE_STATE_PERMISSION,REQUEST_READ_PHONE_STATE);
        }
    }

    private boolean isPermissionGranted(String permission) {
        // проверяем разрешение - есть ли оно у нашего приложения
        int permissionCheck = ActivityCompat.checkSelfPermission(this, permission);
        return permissionCheck == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        if (requestCode == REQUEST_READ_PHONE_STATE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(MainActivity.this, "Разрешения получены",Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(MainActivity.this, "Разрешения не получены",Toast.LENGTH_LONG).show();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    private void requestPermission(String permission, int requestCode) {
        // запрашиваем разрешение
        ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);
    }
}
Если запустить приложение, то получим такой результат:
runtime permissions android api 23
На первом экране мы запрашиваем разрешение. 2 — успешное их получение. 3 — Разрешение не получено
Задумка с разрешениями сводится к тому, что если пользователь не разрешит приложению доступ к чему-то, то мы должны отключить эту функциональность в приложении и работать с остальным функционалом приложения. А как же быть, если запрашиваемое разрешение является критическим для нашего приложения?
Например, работа со звонками является основой приложения, а пользователь запретил доступ. В этом случае при следующем запуске пользователю следует давать возможность изменить свой выбор, иначе — приложение закрывается. Давайте реализуем эту функциональность в нашем приложении:
Новую функциональность я специально выделил:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package com.javadevblog.runtimepermissionsapp;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_READ_PHONE_STATE = 10001;

    // объявляем разрешение, которое нам нужно получить
    private static final String READ_PHONE_STATE_PERMISSION =Manifest.permission.READ_PHONE_STATE;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // проверяем разрешения: если они уже есть,
        // то приложение продолжает работу в нормальном режиме
        if (isPermissionGranted(READ_PHONE_STATE_PERMISSION)) {
            Toast.makeText(this, "Разрешения есть, можно работать",Toast.LENGTH_SHORT).show();
        } else {
            // иначе запрашиваем разрешение у пользователя
            requestPermission(READ_PHONE_STATE_PERMISSION,REQUEST_READ_PHONE_STATE);
        }
    }

    private boolean isPermissionGranted(String permission) {
        // проверяем разрешение - есть ли оно у нашего приложения
        int permissionCheck = ActivityCompat.checkSelfPermission(this, permission);
        return permissionCheck == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        if (requestCode == REQUEST_READ_PHONE_STATE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(MainActivity.this, "Разрешения получены",Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(MainActivity.this, "Разрешения не получены",Toast.LENGTH_LONG).show();
                showPermissionDialog(MainActivity.this);
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    private void requestPermission(String permission, int requestCode) {
        // запрашиваем разрешение
        ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);
    }

    private void showPermissionDialog(Context context) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        String title = getResources().getString(R.string.app_name);
        builder.setTitle(title);
        builder.setMessage(title + " требует разрешение на доступ к звонкам");

        String positiveText = "Настройки";
        builder.setPositiveButton(positiveText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                openAppSettings();
            }
        });

        String negativeText = "Выход";
        builder.setNegativeButton(negativeText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                finish();
            }
        });
        AlertDialog dialog = builder.create();
        // display dialog
        dialog.show();
    }

    private void openAppSettings() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, REQUEST_READ_PHONE_STATE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_READ_PHONE_STATE) {
            requestApplicationConfig();
        }
    }

    private void requestApplicationConfig() {
        if (isPermissionGranted(READ_PHONE_STATE_PERMISSION)) {
            Toast.makeText(MainActivity.this, "Теперь уже разрешения получены",Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(MainActivity.this, "Пользователь снова не дал нам разрешение",Toast.LENGTH_LONG).show();
            requestPermission(READ_PHONE_STATE_PERMISSION,REQUEST_READ_PHONE_STATE);
        }
    }
}
В коде выше мы создали свой AlertDialog, в котором просим пользователя пойти в настройки и все-таки включить нужное разрешение, иначе приложение просто закончит работу.
Запустим приложение на устройстве с Android 6 (API 23) и посмотрим результат:
Результат здесь
Пользователь отклоняет запрос на получение разрешение, после чего приложение просит его перейти в настройки и все-таки дать разрешения — иначе оно просто закрывается

Исходный код приложения здесь.

Обновляйте свои приложения до API 23 и выше с механизмом Android Runtime Permissions. Подписывайтесь на новые статьи по разработке под Android и Java программированию!

Комментариев нет:

Отправить комментарий