مرحبًا بك في مثال تصاريح تشغيل Android. مع إطلاق Android 6.0 Marshmallow، قامت Google بتغيير طريقة معالجة التصاريح من قبل التطبيق. في هذا البرنامج التعليمي، سنتعرف على تصاريح تشغيل Android الجديدة التي تم إدخالها وكيفية التعامل معها. إذا لم يتم التعامل معها بشكل صحيح، يمكن أن تتسبب في تعطل التطبيقات.
ما هي تصاريح تشغيل Android؟
مع إطلاق Android 6.0 (SDK 23)، يُطلب من المستخدمين بعض التصاريح الخاصة في وقت التشغيل عندما تصبح ضرورية للاستخدام. لذلك، السؤال الأول الذي يتبادر إلى أذهاننا هو – هل ستعمل التطبيقات القديمة على Android Marshmallow؟ الجواب هو نعم إذا كان targetSdkVersion هو 22 أو أقل. بالتالي، تدعم تصاريح تشغيل Android التوافق مع الإصدارات السابقة. الآن هذا لا يعني أننا يمكننا العمل بنموذج الأذونات القديمة عن طريق ضبط إصدار sdk على 22. يمكن لمستخدم يستخدم Marshmallow سحب التصاريح الخطرة (سنناقش التصاريح الخطرة والعادية لاحقًا) من الإعدادات-> التطبيقات-> التصاريح. في حالة محاولتنا الاتصال ببعض الوظائف التي تتطلب تصريحًا لم يمنحه المستخدم بعد، سيقوم الوظيفة برمي استثناء(java.lang.SecurityException
) على الفور مما يؤدي إلى تعطل التطبيق. لذلك، نحتاج إلى تنفيذ نموذج تصاريح Android الجديد هذا في تطبيقنا.
الأذونات الخطرة والعادية لنظام Android
يعرف نظام Android بعض الأذونات كخطرة وبعضها كعادية. الشيء المشترك بين النوعين هو أنه يجب تعريفها في ملف التكوين. اعتبارًا من Android 6.0، يتم فحص الأذونات الخطرة فقط أثناء التشغيل، ولا يتم فحص الأذونات العادية. مثال على إذن عادي هو android.permission.INTERNET
. يتم تقسيم الأذونات الخطرة إلى فئات تسهل على المستخدم فهم ما يسمحون للتطبيق بفعله. إذا قبل المستخدم إذنًا واحدًا في مجموعة/فئة ما، فإنه يقبل الفئة بأكملها. مثال على إذن خطير هو android.permission.FINE_LOCATION
و android.permission.COARSE_LOCATION
. تمكين أيًا من أذونات الموقع يمكّن الكل.
طلب أذونات تشغيل Android Runtime
الطريقة requestPermissions(String[] permissions, int requestCode);
هي طريقة عامة تستخدم لطلب الأذونات الخطرة. يمكننا طلب عدة أذونات خطرة عن طريق تمرير مصفوفة سلسلة من الأذونات. ملاحظة: أذونات Android التابعة لمجموعتين مختلفتين ستقوم بعرض مربع حوار فردي لكل منهما على حدة. إذا كانوا ينتمون إلى نفس المجموعة، فسيتم عرض مربع حوار واحد فقط. سيتم تمرير نتائج الطلبات إلى الطريقة onRequestPermissionResult
. مثال: لنفترض أننا نريد الوصول إلى الكاميرا والموقع في تطبيقنا. كلاهما أذونات خطرة. سنقوم بعرض مربع حوار يطلب الوصول إلى هذه الأذونات عند تشغيل التطبيق. لنضيف الأذونات إلى مصفوفة سلسلة ونقوم بالاتصال بـ requestPermissions كما هو موضح أدناه:
String[] perms = {"android.permission.FINE_LOCATION", "android.permission.CAMERA"};
int permsRequestCode = 200;
requestPermissions(perms, permsRequestCode);
@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
switch(permsRequestCode){
case 200:
boolean locationAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;
break;
}
}
الآن لا نريد من المستخدم أن يوافق على الأذونات التي قبلها بالفعل. حتى لو تم منح الإذن مسبقًا، من الضروري التحقق مرة أخرى للتأكد من أن المستخدم لم يسحب ذلك الإذن في وقت لاحق. لذلك، يجب استدعاء الطريقة التالية على كل إذن.
checkSelfPermission(String perm);
تعيد قيمة صحيحة لـ PERMISSION_GRANTED أو PERMISSION_DENIED. ملاحظة: إذا رفض المستخدم إذنًا حاسمًا في التطبيق، فيتم استخدام shouldShowRequestPermissionRationale(String permission);
لشرح ضرورة الإذن للمستخدم. لنقم بتطوير تطبيق يتحقق مما إذا كان الإذن موجودًا بالفعل. إذا لم يكن كذلك، سيتم طلبه أثناء التشغيل.
بنية مشروع أذونات تشغيل Android Runtime
كود أذونات تشغيل Android Runtime
تحتوي content_main.xml
على زرين للتحقق من الأذونات وطلبها.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.journaldev.runtimepermissions.MainActivity"
tools:showIn="@layout/activity_main">
<Button
android:id="@+id/check_permission"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
android:text="Check Permission"/>
<Button
android:id="@+id/request_permission"
android:layout_below="@+id/check_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Request Permission"/>
</RelativeLayout>
تم تعريف MainActivity.java
كما يلي.
package com.journaldev.runtimepermissions;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.CAMERA;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int PERMISSION_REQUEST_CODE = 200;
private View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button check_permission = (Button) findViewById(R.id.check_permission);
Button request_permission = (Button) findViewById(R.id.request_permission);
check_permission.setOnClickListener(this);
request_permission.setOnClickListener(this);
}
@Override
public void onClick(View v) {
view = v;
int id = v.getId();
switch (id) {
case R.id.check_permission:
if (checkPermission()) {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(view, "Please request permission.", Snackbar.LENGTH_LONG).show();
}
break;
case R.id.request_permission:
if (!checkPermission()) {
requestPermission();
} else {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
}
break;
}
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(getApplicationContext(), ACCESS_FINE_LOCATION);
int result1 = ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0) {
boolean locationAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (locationAccepted && cameraAccepted)
Snackbar.make(view, "Permission Granted, Now you can access location data and camera.", Snackbar.LENGTH_LONG).show();
else {
Snackbar.make(view, "Permission Denied, You cannot access location data and camera.", Snackbar.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
showMessageOKCancel("You need to allow access to both the permissions",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{ACCESS_FINE_LOCATION, CAMERA},
PERMISSION_REQUEST_CODE);
}
}
});
return;
}
}
}
}
break;
}
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
}
ملحوظة: أضف الأذونات التي يجب التحقق منها أثناء التشغيل في ملف Manifest فوق علامة التطبيق كما يلي؛
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
في الشيفرة أعلاه، تتم فحص وطلب صلاحيتين هما الكاميرا والموقع. استيراد اسم الصف الثابت للصلاحية يسمح لنا بكتابة OBJECT الصلاحية بدلاً من المسار المؤهل بالكامل. تستدعي `checkPermission()` دالة `checkSelfPermission` على كل من الصلاحيات. تستدعي `requestPermission()` دالة `ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);`. تتحقق `onRequestPermissionsResult` مما إذا تم منح الصلاحيات أم لا. في شيفرتنا، إذا لم يتم منح كلتا الصلاحيتين، يتم عرض مربع حوار تنبيه يظهر الحاجة الإلزامية لطلب الصلاحيات. للقيام بذلك، يتم استدعاء `shouldShowRequestPermissionRationale(String permission)` الذي يقوم بعرض مربع حوار يظهر الحاجة إلى الصلاحيات. يمكنك إلغاء الصلاحيات يدوياً من الإعدادات->التطبيقات->الصلاحيات. ملاحظة: تتوفر الطرق الخاصة بالصلاحيات أثناء التشغيل فقط منذ API 23. لذا يتم فحص الشرط التالي في كل من الطرق:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
يتم توفير نتيجة تطبيق مثال الصلاحيات أثناء التشغيل في الأندرويد أدناه. وهذا ينهي هذا البرنامج التعليمي. يمكنك تنزيل مشروع الصلاحيات أثناء التشغيل النهائي لنظام الأندرويد من الرابط أدناه.
تحميل مشروع مثال الصلاحيات أثناء التشغيل لنظام الأندرويد
المرجع: https://developer.android.com/training/permissions/requesting.html
Source:
https://www.digitalocean.com/community/tutorials/android-runtime-permissions-example