Sử dụng thư viện Dexter để quản lý Runtime Permissions trong Android
Bài đăng này đã không được cập nhật trong 5 năm
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_width="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_width="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_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="CAMERA PERMISSION" />
<Button
android:id="@+id/btn_storage"
android:layout_width="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);
}
}
All rights reserved