Android M - kiểm tra quyền thời gian chạy - làm thế nào để xác định xem người dùng có kiểm tra không bao giờ hỏi lại không?


307

Theo điều này: http://developer.android.com/preview/features/r nb- permissions.html# coding một ứng dụng có thể kiểm tra quyền truy cập và yêu cầu quyền nếu nó chưa được cấp. Hộp thoại sau sẽ được hiển thị sau đó:

nhập mô tả hình ảnh ở đây

Trong trường hợp người dùng từ chối một quyền quan trọng, imo một ứng dụng sẽ hiển thị một lời giải thích tại sao sự cho phép đó là cần thiết và sự suy giảm tác động có gì. Hộp thoại đó có hai tùy chọn:

  1. thử lại lần nữa (quyền được yêu cầu lại)
  2. từ chối (ứng dụng sẽ hoạt động mà không có sự cho phép đó).

Never ask againTuy nhiên, nếu người dùng kiểm tra , hộp thoại thứ hai với lời giải thích không nên được hiển thị, đặc biệt nếu người dùng đã từ chối một lần trước đó. Bây giờ câu hỏi là: làm thế nào để ứng dụng của tôi biết liệu người dùng đã kiểm tra Never ask againchưa? IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)không cung cấp cho tôi thông tin đó.

Câu hỏi thứ hai sẽ là: Google có kế hoạch kết hợp một thông điệp tùy chỉnh trong hộp thoại cấp phép để giải thích lý do tại sao ứng dụng cần sự cho phép không? Bằng cách đó, sẽ không bao giờ có một hộp thoại thứ hai chắc chắn sẽ tạo ra một ux tốt hơn.


9
"Google có kế hoạch kết hợp một thông báo tùy chỉnh trong hộp thoại cấp phép để giải thích lý do tại sao ứng dụng cần sự cho phép không?" - trong bài trình bày Google I | O về hệ thống cấp phép M, tôi dường như nhớ lại ai đó đã hỏi trong Hỏi & Đáp và câu trả lời là họ đang nghĩ về nó.
CommonsWare

1
Không tự kiểm tra, nhưng tài liệu nói về Activity.shouldShowRequestPermissionRationale (Chuỗi): Phương thức này trả về đúng nếu ứng dụng đã yêu cầu quyền này trước đó và người dùng từ chối yêu cầu. Điều đó chỉ ra rằng bạn có thể nên giải thích cho người dùng lý do tại sao bạn cần sự cho phép. Nếu trước đây người dùng đã từ chối yêu cầu cấp phép và chọn tùy chọn Không hỏi lại trong hộp thoại hệ thống yêu cầu quyền, phương thức này trả về sai. Phương thức cũng trả về false nếu chính sách thiết bị cấm ứng dụng có quyền đó.
Sợ

1
@Fợ: có vẻ như họ đã thêm phần này với bản xem trước # 2 của Android M: developer.android.com/preview/support.html#preview2-notes và đó có lẽ là thứ tôi đang tìm kiếm. Tôi không thể kiểm tra nó ngay bây giờ nhưng sẽ làm như vậy vào tuần tới. Nếu nó làm những gì tôi hy vọng nó làm, bạn có thể đăng nó như một câu trả lời và nhận được một số danh tiếng. Trong khi đó, điều này có thể giúp những người khác: youtube.com/watch?v=f17qe9vZ8RM
Emanuel Moecklin 21/07/2015

ví dụ về Quyền nguy hiểm và Quyền đặc biệt: github.com/henrychuangtw/AndroidR.78Permission
HenryChuang

1
@Alex khó hơn cho các nhà phát triển chắc chắn nhưng từ góc độ người dùng có thể cấp hoặc từ chối các quyền cụ thể có ý nghĩa. Vấn đề chính tôi thấy là mức độ chi tiết của các quyền rất không nhất quán và cuối cùng bạn yêu cầu một quyền mà hầu như không liên quan gì đến những gì bạn đang cố gắng thực hiện trong ứng dụng của mình (ví dụ: quyền liên hệ khi tôi muốn kết nối với Google Drive vì cần danh sách tài khoản thiết bị cho mục đích xác thực và quyền tài khoản là một phần của nhóm quyền liên hệ).
Emanuel Moecklin

Câu trả lời:


341

Bản xem trước dành cho nhà phát triển 2 mang đến một số thay đổi về cách yêu cầu quyền của ứng dụng (xem thêm http://developer.android.com/preview/support.html#preview2-notes ).

Hộp thoại đầu tiên bây giờ trông như thế này:

nhập mô tả hình ảnh ở đây

Không có hộp kiểm "Không bao giờ hiển thị lại" (không giống như bản xem trước của nhà phát triển 1). Nếu người dùng từ chối quyền và nếu quyền là cần thiết cho ứng dụng, thì người dùng có thể đưa ra một hộp thoại khác để giải thích lý do ứng dụng yêu cầu quyền đó, ví dụ như sau:

nhập mô tả hình ảnh ở đây

Nếu người dùng từ chối một lần nữa, ứng dụng sẽ tắt nếu nó thực sự cần sự cho phép đó hoặc tiếp tục chạy với chức năng hạn chế. Nếu người dùng xem xét lại (và chọn thử lại), quyền lại được yêu cầu. Lần này lời nhắc trông như thế này:

nhập mô tả hình ảnh ở đây

Lần thứ hai hộp kiểm "Không bao giờ hỏi lại" được hiển thị. Nếu người dùng từ chối một lần nữa và hộp kiểm được đánh dấu thì không có gì xảy ra nữa. Việc hộp kiểm có được đánh dấu hay không có thể được xác định bằng cách sử dụng Activity.shouldShowRequestPermissionRationale (Chuỗi), ví dụ như sau:

if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...

Đó là những gì tài liệu Android nói ( https://developer.android.com/training/permissions/requesting.html ):

Để giúp tìm các tình huống mà bạn cần cung cấp giải thích thêm, hệ thống cung cấp phương thức Activity.shouldShowRequestPermissionRationale (Chuỗi). Phương thức này trả về true nếu ứng dụng đã yêu cầu quyền này trước đó và người dùng từ chối yêu cầu. Điều đó chỉ ra rằng bạn có thể nên giải thích cho người dùng lý do tại sao bạn cần sự cho phép.

Nếu trước đây người dùng đã từ chối yêu cầu cấp phép và chọn tùy chọn Không hỏi lại trong hộp thoại hệ thống yêu cầu quyền, phương thức này trả về sai. Phương thức cũng trả về false nếu chính sách thiết bị cấm ứng dụng có quyền đó.

Để biết liệu người dùng có từ chối "không bao giờ hỏi lại" hay không, bạn có thể kiểm tra lại phương thức ShouldShowRequestPermissionRationale trong onRequestPermissionsResult khi người dùng không cấp quyền.

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == REQUEST_PERMISSION) {
        // for each permission check if the user granted/denied them
        // you may want to group the rationale in a single dialog,
        // this is just an example
        for (int i = 0, len = permissions.length; i < len; i++) {
            String permission = permissions[i];
            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
            // user rejected the permission
                boolean showRationale = shouldShowRequestPermissionRationale( permission );
                if (! showRationale) {
                    // user also CHECKED "never ask again"
                    // you can either enable some fall back,
                    // disable features of your app
                    // or open another dialog explaining
                    // again the permission and directing to
                    // the app setting
                } else if (Manifest.permission.WRITE_CONTACTS.equals(permission)) {
                    showRationale(permission, R.string.permission_denied_contacts);
                    // user did NOT check "never ask again"
                    // this is a good place to explain the user
                    // why you need the permission and ask if he wants
                    // to accept it (the rationale)
                } else if ( /* possibly check more permissions...*/ ) {
                }
            }
        }
    }
}

Bạn có thể mở cài đặt ứng dụng của mình bằng mã này:

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);

Không có cách nào để gửi người dùng trực tiếp đến trang Ủy quyền.


30
Tôi đã xác minh giá trị trả về của phương thức ShouldShowRequestPermissionRationale () thành false để kiểm tra nếu người dùng chọn "Không bao giờ hỏi lại". Nhưng tôi cũng nhận được giá trị của nó là sai lần đầu tiên khi xin phép. Vì vậy, tôi không thể phân biệt được nếu người dùng chọn hộp kiểm "Không bao giờ hỏi lại" hay không. Xin đề nghị ??
Sagar Trehan

32
Theo sự hiểu biết của tôi, phương thức nênShowRationalePermissionRationale () trả về false trong ba trường hợp: 1. Nếu chúng ta gọi phương thức này lần đầu tiên trước khi xin phép. 2. Nếu người dùng chọn "Không hỏi lại" và từ chối cấp phép. 3. Nếu chính sách thiết bị cấm ứng dụng có quyền đó
Sagar Trehan 7/10/2015

24
Tất cả đều tốt ... nhưng chúng tôi, các nhà phát triển, thực sự cần biết liệu người dùng có nói "không bao giờ hỏi lại" hay không. Tôi có một nút đẹp để truy cập một tính năng. Lần đầu tiên người dùng nhấp: nên hỏi lý do? Không, xin phép. Người dùng từ chối. Người dùng nhấp lại vào nút: lý do? Vâng! Hiển thị lý do, người dùng nói Ok, sau đó từ chối và không bao giờ hỏi lại (ok anh ta là một thằng ngốc, nhưng người dùng thường như vậy). Sau này người dùng nhấn lại nút, lý do? Không, xin phép, không có gì xảy ra cho người dùng. Tôi thực sự cần một cách, ở đó, để nói với người dùng: này anh bạn nếu bạn muốn tính năng này bây giờ hãy vào cài đặt ứng dụng và cho phép.
Daniele Segato

4
Tuyệt vời @EmanuelMoecklin điều này tốt hơn sau đó Tài liệu Google ngay bây giờ: D
Daniele Segato

4
onRequestPermissionsResult sẽ không được gọi trừ khi bạn yêu cầu sự cho phép. Vì không có hộp kiểm "Không bao giờ hỏi lại" lần đầu tiên được yêu cầu quyền, nênShowRequestPermissionRationale sẽ trả về Đúng (yêu cầu quyền nhưng không bao giờ hỏi lại). Do đó, lý do luôn được hiển thị lần đầu tiên người dùng từ chối cấp phép nhưng sau đó chỉ khi hộp kiểm tra không được đánh dấu.
Emanuel Moecklin

95

Bạn có thể kiểm tra shouldShowRequestPermissionRationale()trong của bạn onRequestPermissionsResult().

nênShowRequestPermissionRationale https://youtu.be/C8lUdPVSzDk?t=2m23s

Kiểm tra xem sự cho phép đã được cấp hay không onRequestPermissionsResult(). Nếu không thì kiểm tra shouldShowRequestPermissionRationale().

  1. Nếu phương thức này trả về truethì hiển thị một lời giải thích rằng tại sao cần có sự cho phép cụ thể này. Sau đó tùy thuộc vào sự lựa chọn của người dùng một lần nữa requestPermissions().
  2. Nếu nó trả về falsethì hiển thị thông báo lỗi rằng quyền không được cấp và ứng dụng không thể tiếp tục hoặc tính năng cụ thể bị vô hiệu hóa.

Dưới đây là mã mẫu.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case STORAGE_PERMISSION_REQUEST:
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted :)
                downloadFile();
            } else {
                // permission was not granted
                if (getActivity() == null) {
                    return;
                }
                if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    showStoragePermissionRationale();
                } else {
                    Snackbar snackbar = Snackbar.make(getView(), getResources().getString(R.string.message_no_storage_permission_snackbar), Snackbar.LENGTH_LONG);
                    snackbar.setAction(getResources().getString(R.string.settings), new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (getActivity() == null) {
                                return;
                            }
                            Intent intent = new Intent();
                            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                            Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
                            intent.setData(uri);
                            OrderDetailFragment.this.startActivity(intent);
                        }
                    });
                    snackbar.show();
                }
            }
            break;
    }
}

Rõ ràng, google maps thực hiện chính xác điều này để cho phép vị trí.


Cảm ơn bạn cho hình ảnh và liên kết Youtube. Nó phù hợp với ít nhiều câu trả lời của riêng tôi. Cần lưu ý rằng câu hỏi đã được hỏi khi chỉ có bản xem trước dành cho nhà phát triển 1 mà không có phương thức ShouldShowRequestPermissionRationale.
Emanuel Moecklin

Tôi là người mới trong Android và tôi muốn sử dụng phương thức onRequestPermissionsResult () này. nhưng tôi nhận được lỗi rằng nó phải thực hiện một phương thức siêu kiểu. bạn có thể cho biết cách sử dụng cái này không
Andrain

39

Đây là một phương pháp hay và dễ dàng để kiểm tra trạng thái cấp phép hiện tại:

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({GRANTED, DENIED, BLOCKED_OR_NEVER_ASKED })
    public @interface PermissionStatus {}

    public static final int GRANTED = 0;
    public static final int DENIED = 1;
    public static final int BLOCKED_OR_NEVER_ASKED = 2;

    @PermissionStatus 
    public static int getPermissionStatus(Activity activity, String androidPermissionName) {
        if(ContextCompat.checkSelfPermission(activity, androidPermissionName) != PackageManager.PERMISSION_GRANTED) {
            if(!ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName)){
                return BLOCKED_OR_NEVER_ASKED;
            }
            return DENIED;
        }
        return GRANTED;
    }

Hãy cẩn thận: trả về BLOCKED_OR_NEVER_ASKED khi bắt đầu ứng dụng đầu tiên, trước khi người dùng chấp nhận / từ chối cấp phép thông qua lời nhắc của người dùng (trên thiết bị sdk 23+)

Cập nhật:

Thư viện hỗ trợ Android bây giờ cũng dường như có một lớp học rất giống nhau android.support.v4.content.PermissionChecker, trong đó có một checkSelfPermission()mà lợi nhuận:

public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;

1
Đối với lần ra mắt đầu tiên, tôi đang lưu trữ một boolean trong các tùy chọn chia sẻ.
Saeid Farivar

5
Điều này luôn trả về BLOCKED_OR_NEVER_ASKEDnếu sự cho phép chưa được yêu cầu.
Saket

6
vâng, đó là lý do của nó được gọi là "BLOCKED_OR_NEVER_ASKED", cũng xem câu cuối cùng
Patrick Favre

3
android.content.pmđã định nghĩa PERMISSION_GRANTED = 0PERMISSION_DENIED = -1. Có lẽ thiết lập BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1hay cái gì đó?
samis

Xem câu trả lời của mVck dưới đây để xử lý cảnh báo.
samis

28

Khi người dùng đã đánh dấu "Không hỏi lại", câu hỏi không thể được hiển thị lại. Nhưng nó có thể được giải thích cho người dùng rằng trước đây anh ta đã từ chối quyền và phải cấp quyền trong cài đặt. Và tham khảo anh ta để cài đặt, với mã sau đây:

@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults) {

    if (grantResults.length > 0
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // now, you have permission go ahead
        // TODO: something

    } else {

        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.READ_CALL_LOG)) {
            // now, user has denied permission (but not permanently!)

        } else {

            // now, user has denied permission permanently!

            Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "You have previously declined this permission.\n" +
                "You must approve this permission in \"Permissions\" in the app settings on your device.", Snackbar.LENGTH_LONG).setAction("Settings", new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + BuildConfig.APPLICATION_ID)));

            }
        });
        View snackbarView = snackbar.getView();
        TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
        textView.setMaxLines(5);  //Or as much as you need
        snackbar.show();

        }

    }
    return;
}

khi di chuyển sang androidX, bạn có thể thay thế android.support.design.R bằng com.google.android.m
vật.R

26

Có thể hữu ích cho ai đó: -

Những gì tôi đã nhận thấy là, nếu chúng ta kiểm tra cờ nênShowRequestPermissionRationale () trong phương thức gọi lại onRequestPermissionsResult (), nó chỉ hiển thị hai trạng thái .

Trạng thái 1: -Return true: - Bất kỳ lúc nào người dùng nhấp vào Từ chối quyền (kể cả lần đầu tiên).

Trạng thái 2: -Returns false: - nếu người dùng chọn thì không bao giờ hỏi lại ".

Liên kết của ví dụ làm việc chi tiết


2
Đây là cách chính xác để phát hiện nếu người dùng đã chọn không bao giờ hỏi lại tùy chọn.
Muhammad Babar

Ah, chìa khóa ở đây là bạn xử lý việc này trong onRequestPermissionsResultchứ không phải khi thực sự yêu cầu sự cho phép.
Joshua Pinter

26

Bạn có thể xác định nó bằng cách kiểm tra xem lý do cho phép sẽ được hiển thị bên trong onRequestPermissionsResult()phương thức gọi lại. Và nếu bạn tìm thấy bất kỳ quyền nào được đặt thành không bao giờ hỏi lại , bạn có thể yêu cầu người dùng cấp quyền từ cài đặt.

Thực hiện đầy đủ của tôi sẽ như dưới đây. Nó hoạt động cho cả yêu cầu cấp phép đơn hoặc nhiều . Sử dụng sau đây hoặc trực tiếp sử dụng thư viện của tôi.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if(permissions.length == 0){
        return;
    }
    boolean allPermissionsGranted = true;
    if(grantResults.length>0){
        for(int grantResult: grantResults){
            if(grantResult != PackageManager.PERMISSION_GRANTED){
                allPermissionsGranted = false;
                break;
            }
        }
    }
    if(!allPermissionsGranted){
        boolean somePermissionsForeverDenied = false;
        for(String permission: permissions){
            if(ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){
                //denied
                Log.e("denied", permission);
            }else{
                if(ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED){
                    //allowed
                    Log.e("allowed", permission);
                } else{
                    //set to never ask again
                    Log.e("set to never ask again", permission);
                    somePermissionsForeverDenied = true;
                }
            }
        }
        if(somePermissionsForeverDenied){
            final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
            alertDialogBuilder.setTitle("Permissions Required")
                    .setMessage("You have forcefully denied some of the required permissions " +
                            "for this action. Please open settings, go to permissions and allow them.")
                    .setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                    Uri.fromParts("package", getPackageName(), null));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivity(intent);
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .setCancelable(false)
                    .create()
                    .show();
        }
    } else {
        switch (requestCode) {
            //act according to the request code used while requesting the permission(s).
        }
    }
}

hii @nabin yêu cầu của tôi là khi tôi nhấp vào nút tải xuống (tải xuống tệp pdf) thời gian đó phải kiểm tra quyền ghi là cho phép hoặc từ chối vậy làm thế nào để sử dụng mã này! bạn có thể hướng dẫn tôi không
Rucha Bhatt Joshi

xin chào @RuchaBhatt Hãy xem thư viện của tôi. github.com/nabinbhandari/Android-Permissions
Nabin Bhandari

15

Nếu bạn muốn phát hiện tất cả các "trạng thái" (lần đầu tiên bị từ chối, vừa bị từ chối, vừa bị từ chối với "Không bao giờ hỏi lại" hoặc bị từ chối vĩnh viễn), bạn có thể làm như sau:

Tạo 2 booleans

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

Đặt cái đầu tiên trước khi xin phép:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Đặt cái thứ hai bên trong phương thức onRequestPermissionsResult của bạn:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Sử dụng "bảng" sau để làm bất cứ điều gì bạn cần trong onRequestPermissionsResult () (sau khi kiểm tra rằng bạn vẫn không có quyền):

// before after
// FALSE  FALSE  =  Was denied permanently, still denied permanently --> App Settings
// FALSE  TRUE   =  First time deny, not denied permanently yet --> Nothing
// TRUE   FALSE  =  Just been permanently denied --> Changing my caption to "Go to app settings to edit permissions"
// TRUE   TRUE   =  Wasn't denied permanently, still not denied permanently --> Nothing

Không có điểm nào trong việc kiểm tra nênShowRequestPermissionRationale trước khi gọi requestPermissions trừ khi bạn muốn hiển thị lý do trước khi yêu cầu sự cho phép. Chỉ hiển thị lý do sau khi người dùng từ chối quyền dường như là cách mà hầu hết các ứng dụng xử lý hiện nay.
Emanuel Moecklin

2
@EmanuelMoecklin, theo như tôi biết đó là cách duy nhất để kiểm tra xem nó đã bị từ chối chưa (bằng cách kiểm tra trước và sau, như được giải thích trong bảng chân lý của tôi) hoặc nếu đó là lần đầu tiên từ chối (trong trường hợp của tôi, tôi chuyển hướng người dùng đến cài đặt ứng dụng nếu bị từ chối vĩnh viễn)
mVck

1
// TRUE FALSEcũng xảy ra khi người dùng cho phép sau khi từ chối trước đó.
samis

11

Tôi đã có cùng một vấn đề và tôi đã tìm ra nó. Để làm cho cuộc sống đơn giản hơn nhiều, tôi đã viết một lớp tiện dụng để xử lý các quyền thời gian chạy.

public class PermissionUtil {
    /*
    * Check if version is marshmallow and above.
    * Used in deciding to ask runtime permission
    * */
    public static boolean shouldAskPermission() {
        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
    }
private static boolean shouldAskPermission(Context context, String permission){
        if (shouldAskPermission()) {
            int permissionResult = ActivityCompat.checkSelfPermission(context, permission);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true;
            }
        }
        return false;
    }
public static void checkPermission(Context context, String permission, PermissionAskListener listener){
/*
        * If permission is not granted
        * */
        if (shouldAskPermission(context, permission)){
/*
            * If permission denied previously
            * */
            if (((Activity)context).shouldShowRequestPermissionRationale(permission)) {
                listener.onPermissionPreviouslyDenied();
            } else {
                /*
                * Permission denied or first time requested
                * */
if (PreferencesUtil.isFirstTimeAskingPermission(context, permission)) {
                    PreferencesUtil.firstTimeAskingPermission(context, permission, false);
                    listener.onPermissionAsk();
                } else {
                    /*
                    * Handle the feature without permission or ask user to manually allow permission
                    * */
                    listener.onPermissionDisabled();
                }
            }
        } else {
            listener.onPermissionGranted();
        }
    }
/*
    * Callback on various cases on checking permission
    *
    * 1.  Below M, runtime permission not needed. In that case onPermissionGranted() would be called.
    *     If permission is already granted, onPermissionGranted() would be called.
    *
    * 2.  Above M, if the permission is being asked first time onPermissionAsk() would be called.
    *
    * 3.  Above M, if the permission is previously asked but not granted, onPermissionPreviouslyDenied()
    *     would be called.
    *
    * 4.  Above M, if the permission is disabled by device policy or the user checked "Never ask again"
    *     check box on previous request permission, onPermissionDisabled() would be called.
    * */
    public interface PermissionAskListener {
/*
        * Callback to ask permission
        * */
        void onPermissionAsk();
/*
        * Callback on permission denied
        * */
        void onPermissionPreviouslyDenied();
/*
        * Callback on permission "Never show again" checked and denied
        * */
        void onPermissionDisabled();
/*
        * Callback on permission granted
        * */
        void onPermissionGranted();
    }
}

Và các phương pháp PreferenceUtil như sau.

public static void firstTimeAskingPermission(Context context, String permission, boolean isFirstTime){
SharedPreferences sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE;
 sharedPreference.edit().putBoolean(permission, isFirstTime).apply();
 }
public static boolean isFirstTimeAskingPermission(Context context, String permission){
return context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE).getBoolean(permission, true);
}

Bây giờ, tất cả những gì bạn cần là sử dụng phương thức * checkPermission * với các đối số phù hợp.

Đây là một ví dụ,

PermissionUtil.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    new PermissionUtil.PermissionAskListener() {
                        @Override
                        public void onPermissionAsk() {
                            ActivityCompat.requestPermissions(
                                    thisActivity,
              new String[]{Manifest.permission.READ_CONTACTS},
                            REQUEST_EXTERNAL_STORAGE
                            );
                        }
@Override
                        public void onPermissionPreviouslyDenied() {
                       //show a dialog explaining permission and then request permission
                        }
@Override
                        public void onPermissionDisabled() {
Toast.makeText(context, "Permission Disabled.", Toast.LENGTH_SHORT).show();
                        }
@Override
                        public void onPermissionGranted() {
                            readContacts();
                        }
                    });

Làm thế nào để ứng dụng của tôi biết liệu người dùng đã kiểm tra "Không bao giờ hỏi lại"?

Nếu người dùng đã chọn Không bao giờ hỏi lại , bạn sẽ nhận được cuộc gọi lại trên onPermissionDisables .

Chúc mừng mã hóa :)


NênShowRequestPermissionRationale tôi có lỗi ở đây, bạn có thể giúp tôi không.
Rucha Bhatt Joshi

Tôi không thể tìm thấy phương pháp này nênShowRequestPermissionRationale có thể tôi không nhận được ngữ cảnh .. nhưng không sao, tôi đã tìm thấy giải pháp thay thế khác .. Cảm ơn bạn đã giúp đỡ :)
Rucha Bhatt Joshi

1
Lỗi của tôi. ShouldShowRequestPermissionRationale có sẵn thông qua Activity, không phải ngữ cảnh. Tôi đã cập nhật câu trả lời của mình bằng cách chuyển ngữ cảnh thành Activity trước khi gọi phương thức đó. Hãy xem thử :)
muthuraj

1
Đây là cách duy nhất để vượt qua giá trị sai đầu tiên được trả về shouldShowRequestPermissionRationale, lưu vào tùy chọn yêu cầu được gửi cho người dùng. Tôi đã có cùng một ý tưởng và tìm thấy câu trả lời của bạn. Người đàn ông tốt công việc
MatPag

4

Giải thích đầy đủ cho mọi trường hợp cho phép

/**
 *    Case 1: User doesn't have permission
 *    Case 2: User has permission
 *
 *    Case 3: User has never seen the permission Dialog
 *    Case 4: User has denied permission once but he din't clicked on "Never Show again" check box
 *    Case 5: User denied the permission and also clicked on the "Never Show again" check box.
 *    Case 6: User has allowed the permission
 *
 */
public void handlePermission() {
    if (ContextCompat.checkSelfPermission(MainActivity.this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        // This is Case 1. Now we need to check further if permission was shown before or not

        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

            // This is Case 4.
        } else {
            // This is Case 3. Request for permission here
        }

    } else {
        // This is Case 2. You have permission now you can do anything related to it
    }
}

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // This is Case 2 (Permission is now granted)
    } else {
        // This is Case 1 again as Permission is not granted by user

        //Now further we check if used denied permanently or not
        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            // case 4 User has denied permission but not permanently

        } else {
            // case 5. Permission denied permanently.
            // You can open Permission setting's page from here now.
        }

    }
}

4

Một chức năng hữu ích để xác định xem một quyền tùy ý có bị chặn yêu cầu không (trong Kotlin):

private fun isPermissionBlockedFromAsking(activity: Activity, permission: String): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
            && !activity.shouldShowRequestPermissionRationale(permission)
            && PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
    }
    return false
}

Việc sử dụng này đòi hỏi phải thiết lập một boolean tùy chọn chia sẻ với tên của quyền bạn mong muốn (ví dụ android.Manifest.permission.READ_PHONE_STATE) truekhi bạn yêu cầu quyền lần đầu tiên.


Giải trình:

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M vì một số mã chỉ có thể được chạy trên cấp API 23+.

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED để kiểm tra chúng tôi chưa có sự cho phép.

!activity.shouldShowRequestPermissionRationale(permission)để kiểm tra xem người dùng có từ chối ứng dụng hỏi lại không. Do các quirks của chức năng này , dòng sau đây cũng được yêu cầu.

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) điều này được sử dụng (cùng với việc đặt giá trị thành đúng theo yêu cầu cấp phép đầu tiên) để phân biệt giữa trạng thái "Không bao giờ được hỏi" và "Không bao giờ hỏi lại", vì dòng trước không trả về thông tin này.


4

Phương thức nênShowRequestPermissionRationale () có thể được sử dụng để kiểm tra xem người dùng đã chọn tùy chọn 'không bao giờ hỏi lại' và từ chối cấp phép. Có rất nhiều ví dụ về mã, vì vậy tôi muốn giải thích cách sử dụng nó cho mục đích như vậy, bởi vì tôi nghĩ tên của nó và cách thực hiện của nó làm cho điều này thực sự phức tạp hơn.

Như đã giải thích trong Quyền yêu cầu trong thời gian chạy , phương thức đó trả về giá trị đúng nếu tùy chọn 'không bao giờ hỏi lại' hiển thị, sai khác; do đó, nó trả về false ngay lần đầu tiên một hộp thoại được hiển thị, sau đó từ lần thứ hai trở lại đúng và chỉ khi người dùng từ chối quyền chọn tùy chọn, tại thời điểm đó, nó sẽ trả lại sai.

Để phát hiện trường hợp như vậy, bạn có thể phát hiện chuỗi false-true-false hoặc (đơn giản hơn) bạn có thể có một cờ theo dõi thời gian ban đầu của hộp thoại được hiển thị. Sau đó, phương thức đó trả về đúng hoặc sai, trong đó sai sẽ cho phép bạn phát hiện khi tùy chọn được chọn.


3

Xin đừng ném đá tôi vì giải pháp này.

Điều này hoạt động nhưng là một chút "hacky".

Khi bạn gọi requestPermissions, đăng ký thời gian hiện tại.

        mAskedPermissionTime = System.currentTimeMillis();

Sau đó trong onRequestPermissionsResult

nếu kết quả không được cấp, kiểm tra lại thời gian.

 if (System.currentTimeMillis() - mAskedPermissionTime < 100)

Vì người dùng không thể nhấp quá nhanh vào nút từ chối, chúng tôi biết rằng anh ta đã chọn "không bao giờ hỏi lại" vì cuộc gọi lại là ngay lập tức.

Sử dụng có nguy cơ của riêng bạn.


Điều gì xảy ra nếu chúng ta thấy hộp thoại được yêu cầu trong 5 phút và sau đó từ chối?
saksham

Vậy thì việc sử dụng này là gì nếu nó không thể đáp ứng yêu cầu cơ bản. Một mã có thể là một hack như được chấp nhận nếu nó đáp ứng rõ ràng tất cả các yêu cầu trong mọi trường hợp khác.
saksham

Vâng, điều này là xấu. Những người kiểm tra tự động như thế này có thể có thể quản lý để nhấp nhanh hơn thế: developer.android.com/training/testing/crawler
stackzebra

2

Tôi đã viết một tốc ký cho yêu cầu cấp phép trong Android M. Mã này cũng xử lý khả năng tương thích ngược với các phiên bản Android cũ hơn.

Tất cả các mã xấu xí được trích xuất thành một Đoạn để đính kèm và tự tách ra Hoạt động yêu cầu quyền. Bạn có thể sử dụng PermissionRequestManagernhư sau:

new PermissionRequestManager()
        // We need a AppCompatActivity here, if you are not using support libraries you will have to slightly change 
        // the PermissionReuqestManager class
        .withActivity(this)

        // List all permissions you need
        .withPermissions(android.Manifest.permission.CALL_PHONE, android.Manifest.permission.READ_CALENDAR)

        // This Runnable is called whenever the request was successfull
        .withSuccessHandler(new Runnable() {
            @Override
            public void run() {
                // Do something with your permissions!
                // This is called after the user has granted all 
                // permissions, we are one a older platform where 
                // the user does not need to grant permissions 
                // manually, or all permissions are already granted

            }
        })

        // Optional, called when the user did not grant all permissions
        .withFailureHandler(new Runnable() {
            @Override
            public void run() {
                // This is called if the user has rejected one or all of the requested permissions
                L.e(this.getClass().getSimpleName(), "Unable to request permission");

            }
        })

        // After calling this, the user is prompted to grant the rights
        .request();

Hãy xem: https://gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa


2
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    switch (requestCode) {
        case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
            if (grantResults.length > 0) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    // Denied
                } else {
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                        // To what you want
                    } else {
                       // Bob never checked click
                    }
                }
            }
        }
    }
}

2

Hãy thử thư viện cấp phép đơn giản này. Nó sẽ xử lý tất cả các hoạt động liên quan đến sự cho phép trong 3 bước dễ dàng. Nó tiết kiệm thời gian của tôi. Bạn có thể hoàn thành tất cả các công việc liên quan đến sự cho phép trong 15 phút .

Nó có thể xử lý Từ chối, Nó có thể xử lý Không bao giờ hỏi lại, Nó có thể gọi cài đặt ứng dụng để xin phép, Nó có thể đưa ra một thông báo Rational, Nó có thể đưa ra một thông báo Từ chối quyền và vv

https://github.com/ParkSangGwon/TedPermission

Bước 1: thêm sự phụ thuộc của bạn

dependencies {
     compile 'gun0912.ted:tedpermission:2.1.1'
     //check the above link for latest libraries
}

Bước 2: Hỏi quyền

TedPermission.with(this)
    .setPermissionListener(permissionlistener)
    .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
    .setPermissions(Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION)
    .check();

Bước 3: Xử lý phản hồi cho phép

PermissionListener permissionlistener = new PermissionListener() {
    @Override
    public void onPermissionGranted() {
        Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onPermissionDenied(ArrayList<String> deniedPermissions) {
        Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show();
    }
};

Tuyệt quá. Nó đã tiết kiệm thời gian của tôi
Vigneswaran A

Đẹp, dễ sử dụng
Uray Febri

2

bạn có thể nghe khá.

Thính giả

interface PermissionListener {
    fun onNeedPermission()
    fun onPermissionPreviouslyDenied(numberDenyPermission: Int)
    fun onPermissionDisabledPermanently(numberDenyPermission: Int)
    fun onPermissionGranted()
}

MainClass cho phép

class PermissionUtil {

    private val PREFS_FILENAME = "permission"
    private val TAG = "PermissionUtil"

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val permissionResult = ActivityCompat.checkSelfPermission(context, permission)
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true
            }
        }
        return false
    }

    fun checkPermission(context: Context, permission: String, listener: PermissionListener) {

        Log.i(TAG, "CheckPermission for $permission")

        if (shouldAskPermission(context, permission)) {

            // Load history permission
            val sharedPreference = context.getSharedPreferences(PREFS_FILENAME, 0)
            val numberShowPermissionDialog = sharedPreference.getInt(permission, 0)

            if (numberShowPermissionDialog == 0) {

                (context as? Activity)?.let {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(it, permission)) {
                        Log.e(TAG, "User has denied permission but not permanently")
                        listener.onPermissionPreviouslyDenied(numberShowPermissionDialog)
                    } else {
                        Log.e(TAG, "Permission denied permanently.")
                        listener.onPermissionDisabledPermanently(numberShowPermissionDialog)
                    }
                } ?: kotlin.run {
                    listener.onNeedPermission()
                }

            } else {
                // Is FirstTime
                listener.onNeedPermission()
            }


            // Save history permission
            sharedPreference.edit().putInt(permission, numberShowPermissionDialog + 1).apply()


        } else {
            listener.onPermissionGranted()
        }

    }
}

Được sử dụng theo cách này

      PermissionUtil().checkPermission(this, Manifest.permission.ACCESS_FINE_LOCATION,
                object : PermissionListener {
                    override fun onNeedPermission() {
                        log("---------------------->onNeedPermission")

//                            ActivityCompat.requestPermissions(this@SplashActivity,
//                                    Array(1) { Manifest.permission.ACCESS_FINE_LOCATION },
//                                    118)

                    }

                    override fun onPermissionPreviouslyDenied(numberDenyPermission: Int) {
                        log("---------------------->onPermissionPreviouslyDenied")
                    }

                    override fun onPermissionDisabledPermanently(numberDenyPermission: Int) {
                        log("---------------------->onPermissionDisabled")
                    }

                    override fun onPermissionGranted() {
                        log("---------------------->onPermissionGranted")
                    }

                })

ghi đè onRequestPermissionsResult trong hoạt động hoặc Fragmnet

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
 if (requestCode == 118) {
        if (permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getLastLocationInMap()
        }
        }
    }

1

Thay vào đó, bạn sẽ nhận được cuộc gọi lại onRequestPermissionsResult()dưới dạng PERMISSION_DENIED khi bạn yêu cầu cấp phép lại trong khi rơi vào tình trạng saishouldShowRequestPermissionRationale()

Từ tài liệu Android:

Khi hệ thống yêu cầu người dùng cấp quyền, người dùng có tùy chọn yêu cầu hệ thống không yêu cầu quyền đó một lần nữa. Trong trường hợp đó, bất cứ khi nào một ứng dụng sử dụng requestPermissions()để yêu cầu sự cho phép đó một lần nữa, hệ thống sẽ ngay lập tức từ chối yêu cầu. Hệ thống gọi onRequestPermissionsResult()phương thức gọi lại của bạn và vượt qua PERMISSION_DENIED, giống như cách mà người dùng đã từ chối rõ ràng yêu cầu của bạn một lần nữa. Điều này có nghĩa là khi bạn gọi requestPermissions(), bạn không thể cho rằng bất kỳ tương tác trực tiếp nào với người dùng đã diễn ra.


1

Bạn có thể sử dụng if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)phương pháp để phát hiện xem có bao giờ yêu cầu được kiểm tra hay không.

Để tham khảo thêm: Kiểm tra này

Để kiểm tra nhiều quyền sử dụng:

  if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
                            showDialogOK("Service Permissions are required for this app",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            switch (which) {
                                                case DialogInterface.BUTTON_POSITIVE:
                                                    checkAndRequestPermissions();
                                                    break;
                                                case DialogInterface.BUTTON_NEGATIVE:
                                                    // proceed with logic by disabling the related features or quit the app.
                                                    finish();
                                                    break;
                                            }
                                        }
                                    });
                        }
                        //permission is denied (and never ask again is  checked)
                        //shouldShowRequestPermissionRationale will return false
                        else {
                            explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
                            //                            //proceed with logic by disabling the related features or quit the app.
                        }

giải thích () phương thức

private void explain(String msg){
        final android.support.v7.app.AlertDialog.Builder dialog = new android.support.v7.app.AlertDialog.Builder(this);
        dialog.setMessage(msg)
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        //  permissionsclass.requestPermission(type,code);
                        startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        finish();
                    }
                });
        dialog.show();
    }

Mã ở trên cũng sẽ hiển thị hộp thoại, sẽ chuyển hướng người dùng đến màn hình cài đặt ứng dụng từ nơi anh ta có thể cấp quyền nếu đã kiểm tra không bao giờ hỏi lại nút.


1

Bạn có thể dùng

shouldShowRequestPermissionRationale()

phía trong

onRequestPermissionsResult()

Xem ví dụ dưới đây:

Kiểm tra nếu nó có quyền khi người dùng nhấp vào nút:

@Override
public void onClick(View v) {
    if (v.getId() == R.id.appCompatBtn_changeProfileCoverPhoto) {
        if (Build.VERSION.SDK_INT < 23) { // API < 23 don't need to ask permission
            navigateTo(MainActivity.class); // Navigate to activity to change photos
        } else {
            if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                // Permission is not granted yet. Ask for permission...
                requestWriteExternalPermission();
            } else {
                // Permission is already granted, good to go :)
                navigateTo(MainActivity.class);
            }
        } 
    }
}

Khi người dùng trả lời hộp thoại cấp phép, chúng tôi sẽ truy cập onRequestPermissionResult:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == WRITE_EXTERNAL_PERMISSION_REQUEST_CODE) {
        // Case 1. Permission is granted.  
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  
            if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                // Before navigating, I still check one more time the permission for good practice.
                navigateTo(MainActivity.class);
            }
        } else { // Case 2. Permission was refused
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                // Case 2.1. shouldShowRequest... returns true because the
                // permission was denied before. If it is the first time the app is running we will 
                // end up in this part of the code. Because he need to deny at least once to get 
                // to onRequestPermissionsResult. 
                Snackbar snackbar = Snackbar.make(findViewById(R.id.relLayout_container), R.string.you_must_verify_permissions_to_send_media, Snackbar.LENGTH_LONG);
                snackbar.setAction("VERIFY", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        ActivityCompat.requestPermissions(SettingsActivity.this
                                , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
                                , WRITE_EXTERNAL_PERMISSION_REQUEST_CODE);
                    }
                });
                snackbar.show();
            } else {
                // Case 2.2. Permission was already denied and the user checked "Never ask again". 
                // Navigate user to settings if he choose to allow this time.
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(R.string.instructions_to_turn_on_storage_permission)
                        .setPositiveButton(getString(R.string.settings), new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", getPackageName(), null);
                                settingsIntent.setData(uri);
                                startActivityForResult(settingsIntent, 7);
                            }
                        })
                        .setNegativeButton(getString(R.string.not_now), null);
                Dialog dialog = builder.create();
                dialog.show();
            }
        }
    }

}

0

Tôi cũng muốn có được thông tin cho dù người dùng đã chọn "không bao giờ hỏi lại". Tôi đã đạt được một "giải pháp gần như" với một lá cờ trông xấu xí, nhưng trước khi tôi nói cho bạn biết, tôi sẽ nói với bạn về động lực của tôi:

Tôi muốn cung cấp các chức năng giới thiệu cho phép ban đầu. Nếu người dùng sử dụng nó và không có quyền, anh ấy / cô ấy sẽ nhận được hộp thoại thứ 1 từ phía trên hoặc cả thứ 2 và thứ 3. Khi người dùng đã chọn 'Không bao giờ hỏi lại', tôi muốn tắt chức năng và hiển thị khác đi. - Hành động của tôi được kích hoạt bởi một mục nhập văn bản spinner, tôi cũng muốn thêm '(Thu hồi quyền)' vào văn bản nhãn được hiển thị. Điều này cho người dùng thấy: 'Có chức năng nhưng tôi không thể sử dụng nó, do cài đặt quyền của tôi.' Tuy nhiên, điều này dường như là không thể, vì tôi không thể kiểm tra xem 'Không bao giờ hỏi lại' đã được chọn hay chưa.

Tôi đã đi đến một giải pháp mà tôi có thể sống với việc chức năng của tôi luôn được bật với kiểm tra quyền hoạt động. Tôi đang hiển thị thông báo Toast trong onRequestPermissionsResult () trong trường hợp có phản hồi tiêu cực nhưng chỉ khi tôi không hiển thị cửa sổ bật lên hợp lý tùy chỉnh của mình. Vì vậy, nếu người dùng đã chọn 'Không bao giờ hỏi lại', anh ta chỉ nhận được tin nhắn bánh mì nướng. Nếu người dùng không muốn chọn 'không bao giờ hỏi lại', anh ta chỉ nhận được lý do tùy chỉnh và cửa sổ bật lên yêu cầu cấp phép bởi hệ thống vận hành nhưng không nướng, vì ba thông báo liên tiếp sẽ quá đau.


0

Tôi phải thực hiện sự cho phép năng động cho máy ảnh. Trong đó có 3 trường hợp có thể xảy ra: 1. Cho phép, 2. Bị từ chối, 3. Đừng hỏi lại.

 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

    for (String permission : permissions) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permission)) {
            //denied
            Log.e("denied", permission);
        } else {
            if (ActivityCompat.checkSelfPermission(getActivity(), permission) == PackageManager.PERMISSION_GRANTED) {
                //allowed
                Log.e("allowed", permission);
            } else {
                //set to never ask again
                Log.e("set to never ask again", permission);
                //do something here.
            }
        }
    }
    if (requestCode != MaterialBarcodeScanner.RC_HANDLE_CAMERA_PERM) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        return;
    }
    if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        mScannerView.setResultHandler(this);
        mScannerView.startCamera(mCameraId);
        mScannerView.setFlash(mFlash);
        mScannerView.setAutoFocus(mAutoFocus);
        return;
    } else {
        //set to never ask again
        Log.e("set to never ask again", permissions[0]);
    }
    DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();
        }
    };
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle("Error")
            .setMessage(R.string.no_camera_permission)
            .setPositiveButton(android.R.string.ok, listener)
            .show();


}

private void insertDummyContactWrapper() {
        int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.CAMERA);
        if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.CAMERA},
                    REQUEST_CODE_ASK_PERMISSIONS);
            return;
        }
        mScannerView.setResultHandler(this);
        mScannerView.startCamera(mCameraId);
        mScannerView.setFlash(mFlash);
        mScannerView.setAutoFocus(mAutoFocus);
    }

private int checkSelfPermission(String camera) {
    if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        return REQUEST_CODE_ASK_PERMISSIONS;
    } else {
        return REQUEST_NOT_CODE_ASK_PERMISSIONS;
    }
}

0

Mở rộng câu trả lời của mVck ở trên, logic sau xác định xem "Không bao giờ hỏi lại" đã được kiểm tra cho Yêu cầu cấp phép nhất định chưa:

bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
    !bStorage && (
        _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
        _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
    );

được trích từ bên dưới (ví dụ đầy đủ xem câu trả lời này )

private bool _bStorageRationaleBefore;
private bool _bStorageRationaleAfter;        
private const int ANDROID_PERMISSION_REQUEST_CODE__SDCARD = 2;
//private const int ANDROID_PERMISSION_REQUEST_CODE__CAMERA = 1;
private const int ANDROID_PERMISSION_REQUEST_CODE__NONE = 0;

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

    switch (requestCode)
    {
        case ANDROID_PERMISSION_REQUEST_CODE__SDCARD:               
            _bStorageRationaleAfter = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            bool bStorage = grantResults[0] == Permission.Granted;
            bool bNeverAskForStorage =
                !bStorage && (
                    _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
                    _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
                );      
            break;                
    }
}

private List<string> GetRequiredPermissions(out int requestCode)
{
    // Android v6 requires explicit permission granting from user at runtime for security reasons            
    requestCode = ANDROID_PERMISSION_REQUEST_CODE__NONE; // 0
    List<string> requiredPermissions = new List<string>();

    _bStorageRationaleBefore = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
    Permission writeExternalStoragePerm = ApplicationContext.CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage);
    //if(extStoragePerm == Permission.Denied)
    if (writeExternalStoragePerm != Permission.Granted)
    {
        requestCode |= ANDROID_PERMISSION_REQUEST_CODE__SDCARD;
        requiredPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
    }

    return requiredPermissions;
}

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

        // Android v6 requires explicit permission granting from user at runtime for security reasons
        int requestCode;
        List<string> requiredPermissions = GetRequiredPermissions(out requestCode);
        if (requiredPermissions != null && requiredPermissions.Count > 0)
        {
            if (requestCode >= ANDROID_PERMISSION_REQUEST_CODE__SDCARD)                    
            {
                _savedInstanceState = savedInstanceState;
                RequestPermissions(requiredPermissions.ToArray(), requestCode);
                return;
            }
        }
    }            

    OnCreate2(savedInstanceState);
}


0

Để trả lời chính xác câu hỏi, Điều gì xảy ra khi người dùng nhấn "Không bao giờ hỏi lại"?

Phương thức / hàm bị ghi đè

onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)

Mảng GrantResult xuất hiện để trống, vì vậy bạn có thể làm gì đó ở đó không? Nhưng không phải là thực hành tốt nhất.

Làm thế nào để xử lý "Không bao giờ hỏi lại"?

Tôi đang làm việc với Fragment, cần có sự cho phép READ_EXTERNAL_STORAGE.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        when {
            isReadPermissionsGranted() -> {

                /**
                 * Permissions has been Granted
                 */

                getDirectories()
            }

            isPermissionDeniedBefore() -> {

                /**
                 * User has denied before, explain why we need the permission and ask again
                 */

                updateUIForDeniedPermissions()
                checkIfPermissionIsGrantedNow()

            }
            else -> {

                /**
                 * Need to ask For Permissions, First Time
                 */

                checkIfPermissionIsGrantedNow()

                /**
                 * If user selects, "Dont Ask Again" it will never ask again! so just update the UI for Denied Permissions
                 */

                updateUIForDeniedPermissions()

            }
        }
    }

Các chức năng khác là tầm thường.

// Is Read Write Permissions Granted
fun isReadWritePermissionGranted(context: Context): Boolean {
    return (ContextCompat.checkSelfPermission(
        context as Activity,
        Manifest.permission.READ_EXTERNAL_STORAGE
    ) == PackageManager.PERMISSION_GRANTED) and
            (ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) == PackageManager.PERMISSION_GRANTED)
}

fun isReadPermissionDenied(context: Context) : Boolean {
    return ActivityCompat.shouldShowRequestPermissionRationale(
        context as Activity,
        PermissionsUtils.READ_EXTERNAL_STORAGE_PERMISSIONS)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.