Sử dụng thư viện Dexter để quản lý Runtime Permissions trong Android
Chúng ta đều biết rằng Android Marshmallow đã giới thiệu runtime permissions cho phép người dùng cho phép hoặc từ chối mọi quyền trong thời gian chạy. Trong bài viết này, chúng ta sẽ đơn giản hóa việc xin quyền sử dụng các permission bằng cách sử dụng thư viện Dexter. Sử dụng thư viện này, các ...
Chúng ta đều biết rằng Android Marshmallow đã giới thiệu runtime permissions cho phép người dùng cho phép hoặc từ chối mọi quyền trong thời gian chạy.
Trong bài viết này, chúng ta sẽ đơn giản hóa việc xin quyền sử dụng các permission bằng cách sử dụng thư viện Dexter. Sử dụng thư viện này, các permission sẽ được thực hiện chỉ trong vài phút.
1. Sử dụng thư viện Dexter
Để sử dụng thư viện Dexter, thêm dependency vào app/build.gradle của project :
dependencies { // Dexter runtime permissions implementation 'com.karumi:dexter:4.2.0' }
1.1 Request một Permission
Để request một permission, bạn có thể dùng hàm withPermission() bằng cách truyền vào permission cần yêu cầu. Bạn cũng cần callback PermissionListener để nhận về trạng thái của permission.
> onPermissionGranted() sẽ được gọi sau khi permission được cấp phép. > onPermissionDenied() sẽ được gọi khi permission bị từ chối. Bạn có thể kiểm tra xem permission đó có bị từ chối vĩnh viễn hay không bằng cách sử dụng điều kiện answer.isPternalDenied().
Đoạn code sau sẽ yêu cầu sử dụng permission CAMERA :
Dexter.withActivity(this) .withPermission(Manifest.permission.CAMERA) .withListener(new PermissionListener() { @Override public void onPermissionGranted(PermissionGrantedResponse response) { // permission is granted, open the camera } @Override public void onPermissionDenied(PermissionDeniedResponse response) { // check for permanent denial of permission if (response.isPermanentlyDenied()) { // navigate user to app settings } } @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) { token.continuePermissionRequest(); } }).check();
1.2 Request nhiều Permission
Để request nhiều permission cùng một lúc, bạn có thể sử dụng hàm withPermissions().
Đoạn code sau sẽ yêu cầu sử dụng permission STORAGE và LOCATION :
Dexter.withActivity(this) .withPermissions( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION) .withListener(new MultiplePermissionsListener() { @Override public void onPermissionsChecked(MultiplePermissionsReport report) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { // do you work now } // check for permanent denial of any permission if (report.isAnyPermissionPermanentlyDenied()) { // permission is denied permenantly, navigate user to app settings } } @Override public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) { token.continuePermissionRequest(); } }) .onSameThread() .check();
1.3 Xử lí lỗi xảy ra
Dexter.withActivity(this) .withPermissions( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION) .withListener(listener) .withErrorListener(new PermissionRequestErrorListener() { @Override public void onError(DexterError error) { Toast.makeText(getApplicationContext(), "Error occurred! " + error.toString(), Toast.LENGTH_SHORT).show(); } }) .check();
2. Tạo Projetc mới
1. Android Studio ⇒ File ⇒ New Project ⇒ Basic Activity
- Thêm thư viện Dexter vào file build.gradle
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' // ... // Dexter runtime permissions implementation 'com.karumi:dexter:4.2.0' }
- Mở layout của Main Activity : activity_main.xml và content_main.xml
content_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_awidth="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="info.androidhive.dexterpermissions.MainActivity" tools:showIn="@layout/activity_main"> <LinearLayout android:layout_awidth="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="100dp" android:orientation="vertical" android:layout_centerHorizontal="true" android:paddingLeft="16dp" android:paddingRight="16dp"> <Button android:id="@+id/btn_camera" android:layout_awidth="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:text="CAMERA PERMISSION" /> <Button android:id="@+id/btn_storage" android:layout_awidth="match_parent" android:layout_height="wrap_content" android:text="MULTIPLE PERMISSIONS" /> </LinearLayout> </RelativeLayout>
- MainActivity.java
import android.Manifest; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.provider.Settings; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.Button; import android.widget.Toast; import com.karumi.dexter.Dexter; import com.karumi.dexter.MultiplePermissionsReport; import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.DexterError; import com.karumi.dexter.listener.PermissionDeniedResponse; import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.PermissionRequestErrorListener; import com.karumi.dexter.listener.multi.MultiplePermissionsListener; import com.karumi.dexter.listener.single.PermissionListener; import java.util.List; public class MainActivity extends AppCompatActivity { private Button btnCamera, btnStorage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); btnCamera = findViewById(R.id.btn_camera); btnStorage = findViewById(R.id.btn_storage); btnCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { requestCameraPermission(); } }); btnStorage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { requestStoragePermission(); } }); } /** * Requesting multiple permissions (storage and location) at once * This uses multiple permission model from dexter * On permanent denial opens settings dialog */ private void requestStoragePermission() { Dexter.withActivity(this) .withPermissions( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION) .withListener(new MultiplePermissionsListener() { @Override public void onPermissionsChecked(MultiplePermissionsReport report) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { Toast.makeText(getApplicationContext(), "All permissions are granted!", Toast.LENGTH_SHORT).show(); } // check for permanent denial of any permission if (report.isAnyPermissionPermanentlyDenied()) { // show alert dialog navigating to Settings showSettingsDialog(); } } @Override public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) { token.continuePermissionRequest(); } }). withErrorListener(new PermissionRequestErrorListener() { @Override public void onError(DexterError error) { Toast.makeText(getApplicationContext(), "Error occurred! ", Toast.LENGTH_SHORT).show(); } }) .onSameThread() .check(); } /** * Requesting camera permission * This uses single permission model from dexter * Once the permission granted, opens the camera * On permanent denial opens settings dialog */ private void requestCameraPermission() { Dexter.withActivity(this) .withPermission(Manifest.permission.CAMERA) .withListener(new PermissionListener() { @Override public void onPermissionGranted(PermissionGrantedResponse response) { // permission is granted openCamera(); } @Override public void onPermissionDenied(PermissionDeniedResponse response) { // check for permanent denial of permission if (response.isPermanentlyDenied()) { showSettingsDialog(); } } @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) { token.continuePermissionRequest(); } }).check(); } /** * Showing Alert Dialog with Settings option * Navigates user to app settings * NOTE: Keep proper title and message depending on your app */ private void showSettingsDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Need Permissions"); builder.setMessage("This app needs permission to use this feature. You can grant them in app settings."); builder.setPositiveButton("GOTO SETTINGS", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); openSettings(); } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.show(); } // navigating user to app settings private void openSettings() { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, 101); } private void openCamera() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, 100); } }