Начиная с Android Marshmallow (API 23), пользователи будут запрашивать разрешения во время работы приложения. Таким образом, пользователь может выбирать, какие разрешения он должен предоставлять, не влияя на поток приложения. В этом уроке я расскажу о том, как запрашивать разрешения времени выполнения в Android M и N, как выполнить запрос, получить его результат и затем обработать его.
В Android существует множество user-permissions
но я остановлюсь только на некоторых наиболее используемых.
Вы можете найти код для этого проекта в github .
Для начала создайте новый проект в Android Studio, выбрав минимальный уровень API 23 и добавив пустое действие . Это будет единственное мероприятие в проекте.
Объявление разрешений
Откройте AndroidManifest.xml и добавьте следующие разрешения:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.CALL_PHONE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
Указанные выше разрешения являются одними из наиболее часто используемых в приложениях для Android, но все они работают аналогичным образом.
С просьбой разрешить
Каждое из этих разрешений будет запрашиваться с помощью кнопки. Обновите код в файле activity_main.xml :
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.theodhor.runtimepermissions.MainActivity"> <LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Location" android:id="@+id/location" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button style="?android:attr/buttonStyleSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Write ExStorage" android:id="@+id/write" android:onClick="ask" android:layout_marginBottom="10dp" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Read ExStorage" android:id="@+id/read" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Call" android:id="@+id/call" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Camera" android:id="@+id/camera" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Get Accounts" android:id="@+id/accounts" android:layout_marginBottom="10dp" android:onClick="ask" /> </LinearLayout> </RelativeLayout>
Это макет, который вы только что создали:
На этом этапе, если пользователь установил приложение, ему потребуются следующие разрешения:
Внутри MainActivity.java , после onCreate
добавьте этот метод:
private void askForPermission(String permission, Integer requestCode) { if (ContextCompat.checkSelfPermission(MainActivity.this, permission) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission)) { //This is called if user has denied the permission before //In this case I am just asking the permission again ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode); } else { ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode); } } else { Toast.makeText(this, "" + permission + " is already granted.", Toast.LENGTH_SHORT).show(); } }
Этот метод запрашивает у пользователя разрешения. Сначала проверяется, предоставлено ли запрашиваемое вами разрешение или нет. Если это так, то приложение показывает тост, говорящий о том, что разрешение уже предоставлено. Если разрешение не предоставлено, он проверяет, отказал ли пользователь в этом разрешении ранее. Если разрешение важно для приложения, его следует запросить снова. Если разрешение не было отказано ранее, выполните запрос, вызвав ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);
Каждый запрос на разрешение требует три параметра. Первый — это context
, второй — String array
разрешений (ей) String array
, а третий — requestCode
типа Integer
. Последний параметр — это произвольный код, прикрепленный к запросу, и может быть любым числом, подходящим для вашего варианта использования. Когда результат возвращается в действие, он содержит этот код и использует его для различения нескольких результатов друг от друга.
Метод готов выполнять запросы, и теперь он должен быть связан с соответствующими кнопками. Каждая из созданных кнопок имеет свойство android:onClick="ask"
.
Вам нужно создать public
метод ask
который будет вызываться при каждом нажатии кнопок. Это код:
public void ask(View v){ switch (v.getId()){ case R.id.location: askForPermission(Manifest.permission.ACCESS_FINE_LOCATION,LOCATION); break; case R.id.call: askForPermission(Manifest.permission.CALL_PHONE,CALL); break; case R.id.write: askForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,WRITE_EXST); break; case R.id.read: askForPermission(Manifest.permission.READ_EXTERNAL_STORAGE,READ_EXST); break; case R.id.camera: askForPermission(Manifest.permission.CAMERA,CAMERA); break; case R.id.accounts: askForPermission(Manifest.permission.GET_ACCOUNTS,ACCOUNTS); break; default: break; } }
Вторым параметром метода askForPermission
являются случайные целые числа. Добавьте их объявления перед onCreate()
:
static final Integer LOCATION = 0x1; static final Integer CALL = 0x2; static final Integer WRITE_EXST = 0x3; static final Integer READ_EXST = 0x4; static final Integer CAMERA = 0x5; static final Integer ACCOUNTS = 0x6; static final Integer GPS_SETTINGS = 0x7;
Теперь приложение может выполнять запросы. Следующим шагом является обработка результатов запроса.
Обработка результатов
Для обработки результатов запроса на разрешение onRequestPermissionsResult
метод onRequestPermissionsResult
. Это код ниже:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED){ switch (requestCode) { //Location case 1: askForGPS(); break; //Call case 2: Intent callIntent = new Intent(Intent.ACTION_CALL); callIntent.setData(Uri.parse("tel:" + "{This is a telephone number}")); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { startActivity(callIntent); } break; //Write external Storage case 3: break; //Read External Storage case 4: Intent imageIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(imageIntent, 11); break; //Camera case 5: Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { startActivityForResult(takePictureIntent, 12); } break; //Accounts case 6: AccountManager manager = (AccountManager) getSystemService(ACCOUNT_SERVICE); Account[] list = manager.getAccounts(); Toast.makeText(this,""+list[0].name,Toast.LENGTH_SHORT).show(); for(int i=0; i<list.length;i++){ Log.e("Account "+i,""+list[i].name); } } Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show(); } }
Когда результат возвращается, он проверяет, было ли предоставлено разрешение или нет. Если это так, то requestCode
передается в оператор switch
для дифференциации результатов. Я добавил дополнительные строки внутри каждого case
чтобы показать дальнейшие шаги, но они всего лишь примеры.
Запрос местоположения
В case 1:
есть функция с именем askForGPS()
. Эта функция предлагает пользователю включить GPS, если он не включен. Если устройство не подключено к GPS после того, как пользователь предоставил разрешение на местоположение, появится диалоговое окно ниже:
Чтобы показать это диалоговое окно, вам нужен GoogleApiClient
. Во-первых, объявите некоторые переменные, добавив эти строки перед onCreate
:
GoogleApiClient client; LocationRequest mLocationRequest; PendingResult<LocationSettingsResult> result;
И создайте client
внутри метода onCreate
:
client = new GoogleApiClient.Builder(this) .addApi(AppIndex.API) .addApi(LocationServices.API) .build();
Теперь клиент собран, ему нужно подключиться при запуске приложения и отключиться при его остановке. Добавьте эти два overriden
метода в MainActivity
:
@Override public void onStart() { super.onStart(); client.connect(); } @Override public void onStop() { super.onStop(); client.disconnect(); }
Наконец, добавьте функцию, которая показывает диалог GPS:
private void askForGPS(){ mLocationRequest = LocationRequest.create(); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); mLocationRequest.setInterval(30 * 1000); mLocationRequest.setFastestInterval(5 * 1000); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest); builder.setAlwaysShow(true); result = LocationServices.SettingsApi.checkLocationSettings(client, builder.build()); result.setResultCallback(new ResultCallback<LocationSettingsResult>() { @Override public void onResult(LocationSettingsResult result) { final Status status = result.getStatus(); switch (status.getStatusCode()) { case LocationSettingsStatusCodes.SUCCESS: break; case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: try { status.startResolutionForResult(MainActivity.this, GPS_SETTINGS); } catch (IntentSender.SendIntentException e) { } break; case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: break; } } }); }
Вывод
В этом руководстве я показал, как запрашивать разрешения и обрабатывать результаты с помощью новой модели разрешений Android. Цель этой новой модели — дать пользователям возможность контролировать то, что должны делать наши приложения. Я хотел бы услышать ваши комментарии и мнения об этом изменении и о том, как справиться с этим ниже.