کار با Permision و اجازه دسترسی ها در اندروید

با نام خخدا وسلام، تو این آموزش میخوام در مورد Persmision ها در اندروید بنویسم.

اندروید یه سیستم عامل privilege separated می باشد. برای نمونه هر اپلیکیشن در اندروید از دیگر اپلیکیشن ها توسط یه Id متمایز جدا می شود، و هر فایل یا دیتا از اپلیکیشن مخصوص همان اپلیکیشن هست و از دید باقی اپ ها خارج است. به همین دلیل اپلیکیشن های اندروید نمی توانند به فایل یا داده حارج از ناحیه خودشون دسترسی داشته باشند مگر اینکه اون فایل یا داده با اپلیکیشن به اشتراک گذاشته شده باشد.

در نتیجه اگر اپلیکیشن نیاز به چیزی خارج از scope خودش داشته باشد، باید یک در خواست به کاربر بدهد. اندروید با تعدادی permision های از پیش تعیین شده ارایه شده است(System Permision). هر اپلیکیشن می تواند permision های مورد نیازش را درخواست دهد. برای مثال یه اپلیکیشت ممکن است درخواست دسترسی به شبکه وایرلس داشته باشد. همچنین می توان permision های جدیدی تعریف کرد.

1- Permision WorkFlow قبل و بعد از اندروید M یا (API 23) :

حال مسئله اینجاست که یه اپلیکیشن چطور میتونه درخواست یه permision رو بده؟؟ یا اینکه یه توسعه دهنده باید چه workflow ای رو برای درخواست یه دسترسی برای یه app بده؟؟ از API23 به بعد این workflow تغییر کرده است.

-مدل دسترسی قبل از اندروید M  یا (API 23) – قبل از اندرویدM مدل برای توسعه دهنده بسیار ساده تر بود اما کنترل و امنیت کمتری را برای کاربر فراهم می کرد. در خواست دسترسی ها قبل از نصب اپلیکیشن به کاربر داده می شد. کاربر تصمیم می گرفت که این درخواست ها را برای اپ تایید کند و نصب اپلیکیشن بپردازد یا در کل قید نصب کردن رو بزنه و اصن نصب نکنه. امکان برداشتن یا افزودن دسترسی ها بعد از نصب وجود نداشت. در نتیجه توسعه دهنده ها تنها بایستی دسترسی های مورد نیاز خودشون رو داحل Android Manifest می نوشتند و تمام، وقتی app اجرا میشد تمام دسترسی ها رو داشت !!!

-مدل دسترسی از اندروید M به بعد – با امدن اندروید 6 مارشمالو (API 23 ) ، یک مدل permision به صورت runtime معرفی شد.مطابق این مدل دیگه کاربر در هنگام نصب بیدرنگ مجبور به اجازه ی دسترسی ها به اپ نیست، در کنار این توسعه دهنده ها مجبور به درخواست دسترسی در هنگام اجرای (runtime) اپلیکیشن شدند(قبل از انجام هر عملی که ممکن است نیاز به دسترسی داشته باشه)، کاربران در نتیجه میتوانند درخواست رو قبول کنند یا رد کنند، همچینین کاربران می توانند دسترسی ها رو هر موقع که خواستند از داخل setting به اپلیکیشن مورد نظر بدهند یه بردارند. منجر به تجربه کاربری ایمن تری در اندروید می شود، اما کار توسعه دهنده ها رو سخت تر کرده است. از این به بعد توسعه دهنده باید تمام usecase های موجود رو مدیریت کند.

2- سطوح دسترسی:

دسترسی های سیستمی سطوح حفاظتی مختلفی دارند. دو دسته بسیار مهم حفاظتی دسترسی های normal و dangerous می باشد.

دسترسی های normal: این دسترسی ها تاثیر بسیار کم یا در حد صفری را روی امنیت یا حریم خصوصی کاربر دارند. سیستم خودش دسترسی های normal رو در هنگام درخواست بدون اطلاع به کاربر به اپلیکیشن می دهد. برای مثال ACCESS_WIFI_STATE, WAKE_LOCK و …

 

دسترسی های Dangerous : این دسترسی ها معمولا تاثیر بیشتری روی حریم خصوصی و امنیت کاربر دارند. برای مثال درخواست توانایی خواندن مخاطبین کاربر READ_CONTACTS . اگر یه اپلیکیشن درحواست dangerous permision بدهد خود کاربر باید به صورت صریح اجازه این دسترسی را به app بدهد.

 

 

اینجا میتونید اطلاعات بیشتری در مورد دسترسی ها و دسته هایشون به دست بیارید

 

خب دیگه از اینجا به ساخت به App نمونه می پردازیم:

3- ساخت پروژه اندریود.

1- من در مورد نحوه ساخت دیگه توضیحی نمیدم، تو مطالب قبلی در مورد اندروید در مورد روش و مراحل ساخت نوشتم 🙂

2- ابتدا build.gradle رو باز کنید (داخل search عبارت build.gradle را جستجو کنید ) و مطمئن بشید که minSdkVersion و targetSdkVersion تعریف شده اند من مقدار 17 را برای minSdkVersion و مقدار 25 رو برای targetSdkVersion قرار دادم.

 

3.1 – درخواست یک دسترسی (Single Permision)

3- خب با اینکه باید دسترسی را در هنگام runtime از کاربر بخواهیم باز باید داخل manifest نیز دسترسی مورد نظرمون رو اضافه کنیم. تو این مثال من دسترسی به نوشتن روی حافظه حارجی رو میخوام اضافه کنم (WRITE_EXTERNAL_STORAGE_PERMISSION). پس ابتدا باید این اجازه دسترسی را به AndroidManifest.xml اضافه کنیم.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.mpermissions">
 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name="com.sadegh_khan.mpermissions.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

4- حال نوبت به main activity میشه، کد زیر رو باید جایی که میخوایم اجازه دسترسی داده بشه بنویسیم :

if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                //Show Information about why you need the permission
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("Need Storage Permission");
                builder.setMessage("This app needs storage permission.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT);
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            } else if (permissionStatus.getBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,false)) {
                //Previously Permission Request was cancelled with 'Dont Ask Again',
                // Redirect to Settings after showing Information about why you need the permission
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("Need Storage Permission");
                builder.setMessage("This app needs storage permission.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        sentToSettings = true;
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", getPackageName(), null);
                        intent.setData(uri);
                        startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                        Toast.makeText(getBaseContext(), "Go to Permissions to Grant Storage", Toast.LENGTH_LONG).show();
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            } else {
                //just request the permission
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT);
            }
 
            SharedPreferences.Editor editor = permissionStatus.edit();
            editor.putBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,true);
            editor.commit();
 
 
        } else {
            //You already have the permission, just go ahead.
            proceedAfterPermission();
        }

5- مقادیر ثابت زیر نیز باید در mainactivity و پس پرانتز باز  شروع کلاس گذاشته شوند:

private static final int EXTERNAL_STORAGE_PERMISSION_CONSTANT = 100;
   private static final int REQUEST_PERMISSION_SETTING = 101;
   private boolean sentToSettings = false;
   private SharedPreferences permissionStatus;
public class MainActivity extends AppCompatActivity {
   private static final int EXTERNAL_STORAGE_PERMISSION_CONSTANT = 100;
   private static final int REQUEST_PERMISSION_SETTING = 101;
   private boolean sentToSettings = false;
   private SharedPreferences permissionStatus;
 
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {

6- متود های زیر نیز باید بعد از onCreate داخل کلاس mainActivity نوشته شوند:

private void proceedAfterPermission() {
       //We've got the permission, now we can proceed further
       Toast.makeText(getBaseContext(), "We got the Storage Permission", Toast.LENGTH_LONG).show();
   }
 
   @Override
   public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
       super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       if (requestCode == EXTERNAL_STORAGE_PERMISSION_CONSTANT) {
           if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               //The External Storage Write Permission is granted to you... Continue your left job...
               proceedAfterPermission();
           } else {
               if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                   //Show Information about why you need the permission
                   AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                   builder.setTitle("Need Storage Permission");
                   builder.setMessage("This app needs storage permission");
                   builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int which) {
                           dialog.cancel();
 
 
                           ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT);
 
 
                       }
                   });
                   builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int which) {
                           dialog.cancel();
                       }
                   });
                   builder.show();
               } else {
                   Toast.makeText(getBaseContext(),"Unable to get Permission",Toast.LENGTH_LONG).show();
               }
           }
       }
   }
 
 
   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
       super.onActivityResult(requestCode, resultCode, data);
       if (requestCode == REQUEST_PERMISSION_SETTING) {
           if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
               //Got Permission
               proceedAfterPermission();
           }
       }
   }
 
 
   @Override
   protected void onPostResume() {
       super.onPostResume();
       if (sentToSettings) {
           if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
               //Got Permission
               proceedAfterPermission();
           }
       }
   }

 

3.2 – درخواست چندین دسترسی: 

7- فایل کلاس mainactivity برای این کار به صورت نمونه در زیر گذاشتم (بخش هایی که مربوط به permision هستن را داخل کامنت :

// Sadegh : Check Permision

 گذاشتم)

package arisa_co.com.free;

import android.Manifest;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.design.widget.TabLayout;
import android.support.design.widget.FloatingActionButton;
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.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

//import info.androidhive.materialtabs.R;
//import info.androidhive.materialtabs.fragments.OneFragment;
//import info.androidhive.materialtabs.fragments.ThreeFragment;
//import info.androidhive.materialtabs.fragments.TwoFragment;

public class MainActivity extends AppCompatActivity {

    // Sadegh : Check Permision
    private static final int PERMISSION_CALLBACK_CONSTANT = 100;
    private static final int REQUEST_PERMISSION_SETTING = 101;
    String[] permissionsRequired = new String[]{Manifest.permission.CAMERA,
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_EXTERNAL_STORAGE};
    private SharedPreferences permissionStatus;
    private boolean sentToSettings = false;
// Sadegh : Check Permision







    private Toolbar toolbar;
    private TabLayout tabLayout;
    private ViewPager viewPager;


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


        // Sadegh : Check Permision
        permissionStatus = getSharedPreferences("permissionStatus",MODE_PRIVATE);
        if(ActivityCompat.checkSelfPermission(MainActivity.this, permissionsRequired[0]) != PackageManager.PERMISSION_GRANTED
                || ActivityCompat.checkSelfPermission(MainActivity.this, permissionsRequired[1]) != PackageManager.PERMISSION_GRANTED
                || ActivityCompat.checkSelfPermission(MainActivity.this, permissionsRequired[2]) != PackageManager.PERMISSION_GRANTED){
            if(ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,permissionsRequired[0])
                    || ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,permissionsRequired[1])
                    || ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,permissionsRequired[2])){
                //Show Information about why you need the permission
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("Need Multiple Permissions");
                builder.setMessage("This app needs Camera and Location permissions.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        ActivityCompat.requestPermissions(MainActivity.this,permissionsRequired,PERMISSION_CALLBACK_CONSTANT);
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            } else if (permissionStatus.getBoolean(permissionsRequired[0],false)) {
                //Previously Permission Request was cancelled with 'Dont Ask Again',
                // Redirect to Settings after showing Information about why you need the permission
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("Need Multiple Permissions");
                builder.setMessage("This app needs Camera and Location permissions.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        sentToSettings = true;
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", getPackageName(), null);
                        intent.setData(uri);
                        startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                        Toast.makeText(getBaseContext(), "Go to Permissions to Grant  Camera and Location", Toast.LENGTH_LONG).show();
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            }  else {
                //just request the permission
                ActivityCompat.requestPermissions(MainActivity.this,permissionsRequired,PERMISSION_CALLBACK_CONSTANT);
            }

            //txtPermissions.setText("Permissions Required");

            SharedPreferences.Editor editor = permissionStatus.edit();
            editor.putBoolean(permissionsRequired[0],true);
            editor.commit();
        } else {
            //You already have the permission, just go ahead.
            proceedAfterPermission();
        }

        // Sadegh : Check Permision



        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        setupViewPager(viewPager);

        tabLayout = (TabLayout) findViewById(R.id.tabs);
        //Assigns the ViewPager to TabLayout.
        tabLayout.setupWithViewPager(viewPager);
        setupTabIcons();
    }





    // Sadegh : Check Permision

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == PERMISSION_CALLBACK_CONSTANT){
            //check if all permissions are granted
            boolean allgranted = false;
            for(int i=0;i<grantResults.length;i++){
                if(grantResults[i]==PackageManager.PERMISSION_GRANTED){
                    allgranted = true;
                } else {
                    allgranted = false;
                    break;
                }
            }

            if(allgranted){
                proceedAfterPermission();
            } else if(ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,permissionsRequired[0])
                    || ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,permissionsRequired[1])
                    || ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,permissionsRequired[2])){
                //txtPermissions.setText("Permissions Required");
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("Need Multiple Permissions");
                builder.setMessage("This app needs Camera and Location permissions.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        ActivityCompat.requestPermissions(MainActivity.this,permissionsRequired,PERMISSION_CALLBACK_CONSTANT);
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            } else {
                Toast.makeText(getBaseContext(),"Unable to get Permission",Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_PERMISSION_SETTING) {
            if (ActivityCompat.checkSelfPermission(MainActivity.this, permissionsRequired[0]) == PackageManager.PERMISSION_GRANTED) {
                //Got Permission
                proceedAfterPermission();
            }
        }
    }

    private void proceedAfterPermission() {
        //txtPermissions.setText("We've got all permissions");
        Toast.makeText(getBaseContext(), "We got All Permissions", Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        if (sentToSettings) {
            if (ActivityCompat.checkSelfPermission(MainActivity.this, permissionsRequired[0]) == PackageManager.PERMISSION_GRANTED) {
                //Got Permission
                proceedAfterPermission();
            }
        }
    }
    // Sadegh : Check Permision


    private void setupTabIcons() {
        //tabLayout.getTabAt(0).setIcon(tabIcons[0]);
        //tabLayout.getTabAt(1).setIcon(tabIcons[1]);
        //tabLayout.getTabAt(2).setIcon(tabIcons[2]);
    }

    //Defines the number of tabs by setting appropriate fragment and tab name.
    private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        adapter.addFragment(new T11Fragment(), "ONE");
        adapter.addFragment(new T12Fragment(), "TWO");
        adapter.addFragment(new T13Fragment(), "THREE");
        adapter.addFragment(new T14Fragment(), "FOUR");
        viewPager.setAdapter(adapter);
    }

    //Custom adapter class provides fragments required for the view pager.
    class ViewPagerAdapter extends FragmentPagerAdapter {
        private final List<Fragment> mFragmentList = new ArrayList<>();
        private final List<String> mFragmentTitleList = new ArrayList<>();

        public ViewPagerAdapter(FragmentManager manager) {
            super(manager);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }

        @Override
        public int getCount() {
            return mFragmentList.size();
        }

        public void addFragment(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentTitleList.get(position);
        }
    }
}