Android M - ตรวจสอบการอนุญาตรันไทม์ - วิธีการตรวจสอบว่าผู้ใช้เลือก“ ไม่ต้องถามอีก”


307

ตามนี้: http://developer.android.com/preview/features/runtime-permissions.html#codingแอปสามารถตรวจสอบสิทธิ์การใช้งานจริงและการขออนุญาตหากยังไม่ได้รับอนุญาต กล่องโต้ตอบต่อไปนี้จะปรากฏขึ้นจากนั้น:

ป้อนคำอธิบายรูปภาพที่นี่

ในกรณีที่ผู้ใช้ปฏิเสธการอนุญาตที่สำคัญ imo แอปควรแสดงคำอธิบายว่าทำไมต้องมีการอนุญาตและสิ่งที่มีผลกระทบต่อการลดลง กล่องโต้ตอบนั้นมีสองตัวเลือก:

  1. ลองอีกครั้งอีกครั้ง (ขอสิทธิ์อีกครั้ง)
  2. ปฏิเสธ (แอปจะทำงานโดยไม่ได้รับอนุญาต)

อย่างไรก็ตามหากผู้ใช้ตรวจสอบNever ask againไม่ควรแสดงกล่องโต้ตอบที่สองพร้อมคำอธิบายโดยเฉพาะอย่างยิ่งหากผู้ใช้เคยปฏิเสธมาก่อน ตอนนี้คำถามคือ: แอพของฉันรู้ได้อย่างไรว่าผู้ใช้ได้ตรวจสอบแล้วNever ask again? IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)ไม่ให้ข้อมูลนั้นแก่ฉัน

คำถามที่สองก็คือ: Google มีแผนที่จะรวมข้อความที่กำหนดเองในช่องโต้ตอบการอนุญาตที่จะอธิบายว่าทำไมแอปจำเป็นต้องได้รับอนุญาตหรือไม่ ด้วยวิธีนี้จะไม่มีการสนทนาครั้งที่สองซึ่งแน่นอนว่าจะทำให้ดีขึ้น ux


9
"Google มีแผนที่จะรวมข้อความที่กำหนดเองในกล่องโต้ตอบการอนุญาตที่จะอธิบายว่าทำไมแอปนั้นต้องการการอนุญาตหรือไม่" - ในการนำเสนอของ Google I | O เกี่ยวกับระบบการอนุญาต M ดูเหมือนว่าฉันจำได้ว่ามีคนถามคำถามและคำตอบคือพวกเขากำลังคิดเกี่ยวกับมัน
CommonsWare

1
ไม่ได้ทดสอบด้วยตนเอง แต่มีเอกสารพูดเกี่ยวกับ Activity.shouldShowRequestPermissionRationale (String): วิธีนี้จะส่งกลับค่าจริงหากแอปขอสิทธิ์นี้ก่อนหน้านี้และผู้ใช้ปฏิเสธคำขอ นั่นหมายความว่าคุณควรอธิบายให้ผู้ใช้ทราบว่าเพราะเหตุใดคุณจึงต้องขออนุญาต หากผู้ใช้ปฏิเสธคำขออนุญาตในอดีตและเลือกตัวเลือกไม่ต้องถามอีกในกล่องโต้ตอบระบบขอสิทธิ์วิธีนี้จะส่งกลับค่าเท็จ วิธีนี้จะส่งคืนค่าเท็จหากนโยบายอุปกรณ์ห้ามไม่ให้แอปได้รับอนุญาต
เกรงกลัว

1
@ กลัว: ดูเหมือนว่าพวกเขาจะเพิ่มสิ่งนี้ด้วยหน้าตัวอย่าง # 2 ของ Android M: developer.android.com/preview/support.html#preview2-notesและอาจเป็นสิ่งที่ฉันกำลังมองหา ฉันไม่สามารถทดสอบได้ในตอนนี้ แต่จะทำในสัปดาห์หน้า หากเป็นไปตามที่ฉันหวังไว้คุณสามารถโพสต์เป็นคำตอบและรับชื่อเสียง ในระหว่างนี้อาจช่วยผู้อื่นได้: youtube.com/watch?v=f17qe9vZ8RM
Emanuel Moecklin

ตัวอย่างของการอนุญาตที่เป็นอันตรายและการอนุญาตพิเศษ: github.com/henrychuangtw/AndroidRuntimePermission
HenryChuang

1
@Alex ยากขึ้นสำหรับนักพัฒนาที่แน่นอน แต่จากมุมมองของผู้ใช้ความสามารถในการให้หรือปฏิเสธสิทธิ์เฉพาะที่เหมาะสม ปัญหาหลักที่ฉันเห็นคือการอนุญาตอย่างละเอียดไม่สอดคล้องกันและคุณท้ายขอการอนุญาตที่อาจไม่มีส่วนเกี่ยวข้องกับสิ่งที่คุณพยายามทำในแอปของคุณ (เช่นสิทธิ์ผู้ติดต่อเมื่อฉันต้องการเชื่อมต่อกับ Google ไดรฟ์เนื่องจากต้องมีรายการบัญชีอุปกรณ์สำหรับการตรวจสอบสิทธิ์และการอนุญาตบัญชีเป็นส่วนหนึ่งของกลุ่มสิทธิ์ในการติดต่อ)
Emanuel Moecklin

คำตอบ:


341

Developer Preview 2 นำการเปลี่ยนแปลงบางอย่างเกี่ยวกับวิธีการขออนุญาตจากแอพ (ดูhttp://developer.android.com/preview/support.html#preview2-notes )

ไดอะล็อกแรกจะมีลักษณะดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

ไม่มีช่องทำเครื่องหมาย "ไม่แสดงอีกเลย" (ไม่เหมือนตัวอย่างนักพัฒนา 1) หากผู้ใช้ปฏิเสธการอนุญาตและหากการอนุญาตเป็นสิ่งจำเป็นสำหรับแอปก็สามารถนำเสนอกล่องโต้ตอบอื่นเพื่ออธิบายเหตุผลที่แอพขออนุญาตนั้นเช่นนี้:

ป้อนคำอธิบายรูปภาพที่นี่

หากผู้ใช้ปฏิเสธอีกครั้งแอปควรปิดหากจำเป็นต้องได้รับการอนุญาตหรือการทำงานที่ จำกัด ต่อไป หากผู้ใช้พิจารณาอีกครั้ง (และเลือกลองอีกครั้ง) ระบบจะขอการอนุญาตอีกครั้ง เวลานี้พรอมต์จะเป็นดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

ครั้งที่สองที่กล่องกาเครื่องหมาย "ไม่ต้องถามอีก" จะปรากฏขึ้น หากผู้ใช้ปฏิเสธอีกครั้งและกล่องกาเครื่องหมายถูกทำเครื่องหมายไม่ควรเกิดอะไรขึ้น ไม่ว่ากล่องกาเครื่องหมายจะถูกเลือกหรือไม่สามารถกำหนดได้โดยใช้ Activity.shouldShowRequestPermissionRationale (String) เช่นนี้:

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

นั่นคือสิ่งที่เอกสาร Android กล่าว ( https://developer.android.com/training/permissions/requesting.html ):

เพื่อช่วยค้นหาสถานการณ์ที่คุณต้องการให้คำอธิบายเพิ่มเติมระบบจะจัดเตรียมเมธอด Activity.shouldShowRequestPermissionRationale (String) วิธีการนี้จะให้ผลเป็นจริงหากแอปขอสิทธิ์นี้ก่อนหน้านี้และผู้ใช้ปฏิเสธคำขอ นั่นหมายความว่าคุณควรอธิบายให้ผู้ใช้ทราบว่าเพราะเหตุใดคุณจึงต้องขออนุญาต

หากผู้ใช้ปฏิเสธคำขออนุญาตในอดีตและเลือกตัวเลือกไม่ต้องถามอีกในกล่องโต้ตอบระบบขอสิทธิ์วิธีนี้จะส่งกลับค่าเท็จ วิธีนี้จะส่งคืนค่าเท็จหากนโยบายอุปกรณ์ห้ามมิให้แอปมีสิทธิ์

หากต้องการทราบว่าผู้ใช้ที่ปฏิเสธด้วย "ไม่ต้องถามอีก" คุณสามารถตรวจสอบวิธีshouldShowRequestPermissionRationaleอีกครั้งในonRequestPermissionsResultของคุณเมื่อผู้ใช้ไม่ได้รับอนุญาต

@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...*/ ) {
                }
            }
        }
    }
}

คุณสามารถเปิดการตั้งค่าแอพของคุณด้วยรหัสนี้:

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

ไม่มีวิธีการส่งผู้ใช้โดยตรงไปยังหน้าการอนุญาต


30
ฉันตรวจสอบค่าส่งคืนของ shouldShowRequestPermissionRationale () วิธีการที่เป็นเท็จสำหรับการตรวจสอบว่าผู้ใช้เลือก "ไม่ต้องถามอีก" แต่ฉันก็ได้รับค่าของมันเป็นเท็จเป็นครั้งแรกเมื่อขออนุญาต ดังนั้นฉันจึงไม่สามารถแยกความแตกต่างได้หากผู้ใช้เลือกช่องทำเครื่องหมาย "ไม่ต้องถามอีก" หรือไม่ กรุณาแนะนำ ??
Sagar Trehan

32
ตามความเข้าใจของฉัน shouldShowRationalePermissionRationale () วิธีการคืนค่าเท็จในสามกรณี: 1. ถ้าเราเรียกวิธีนี้เป็นครั้งแรกก่อนที่จะขออนุญาต 2. หากผู้ใช้เลือก "ไม่ต้องถามอีก" และปฏิเสธการอนุญาต 3. หากนโยบายอุปกรณ์ห้ามไม่ให้แอปได้รับอนุญาต
Sagar Trehan

24
ดีมาก ... แต่เราผู้พัฒนาจำเป็นต้องรู้จริงๆว่าผู้ใช้พูดว่า "ไม่ต้องถามอีก" หรือไม่ ฉันมีปุ่มดีในการเข้าถึงคุณลักษณะ ครั้งแรกที่ผู้ใช้คลิก: ควรถามเหตุผลหรือไม่ ไม่ขออนุญาต ผู้ใช้ปฏิเสธ ผู้ใช้คลิกปุ่มอีกครั้ง: เหตุผล? อ๋อ! แสดงเหตุผลผู้ใช้พูดว่าตกลงแล้วปฏิเสธและไม่ถามอีกเลย (ตกลงเขาเป็นคนงี่เง่า แต่ผู้ใช้มักจะเป็น) ผู้ใช้ในภายหลังกดปุ่มอีกครั้งเหตุผล? ไม่ขออนุญาตไม่มีอะไรเกิดขึ้นกับผู้ใช้ ฉันต้องการวิธีที่นั่นจริงๆเพื่อบอกผู้ใช้: เฮ้ถ้าคุณต้องการให้ฟีเจอร์นี้ไปที่การตั้งค่าแอพและให้อนุญาต
Daniele Segato

4
ยอดเยี่ยม @EmanuelMoecklin ดีกว่า Google เอกสารตอนนี้: D
Daniele Segato

4
onRequestPermissionsResult จะไม่ถูกเรียกจนกว่าคุณจะขออนุญาต เนื่องจากไม่มีกล่องกาเครื่องหมาย "ไม่ต้องถามอีก" ในครั้งแรกที่มีการร้องขอการอนุญาตควรแสดง ShowRequestPermissionRationale จะส่งคืน True (ได้รับอนุญาตที่ขอ แต่ไม่ต้องถามอีก) ดังนั้นเหตุผลจะปรากฏขึ้นเสมอในครั้งแรกที่ผู้ใช้ปฏิเสธการอนุญาต แต่หลังจากนั้นก็ต่อเมื่อไม่มีการทำเครื่องหมายในช่องทำเครื่องหมายเท่านั้น
Emanuel Moecklin

95

คุณสามารถตรวจสอบในของคุณshouldShowRequestPermissionRationale()onRequestPermissionsResult()

shouldShowRequestPermissionRationale https://youtu.be/C8lUdPVSzDk?t=2m23s

onRequestPermissionsResult()ตรวจสอบว่าได้รับอนุญาตหรือไม่ได้อยู่ใน ถ้าไม่ได้shouldShowRequestPermissionRationale()แล้วตรวจสอบ

  1. หากวิธีการนี้กลับtrueมาแสดงคำอธิบายว่าทำไมต้องได้รับอนุญาตโดยเฉพาะ requestPermissions()จากนั้นก็ขึ้นอยู่กับทางเลือกของผู้ใช้อีกครั้ง
  2. หากมันกลับfalseมาแสดงข้อความข้อผิดพลาดที่ไม่ได้รับอนุญาตและแอปไม่สามารถดำเนินการต่อไปหรือคุณลักษณะเฉพาะถูกปิดใช้งาน

ด้านล่างเป็นรหัสตัวอย่าง

@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;
    }
}

เห็นได้ชัดว่าแผนที่ google ทำเช่นนี้เพื่อขออนุญาตสถานที่


ขอบคุณสำหรับรูปภาพและลิงค์ Youtube มันตรงกับคำตอบของฉันเองไม่มากก็น้อย จะต้องมีการตั้งข้อสังเกตว่าคำถามที่ถูกถามเมื่อมีเพียง 1 ตัวอย่างนักพัฒนาที่มีอยู่ซึ่งไม่ได้มีวิธีการควรจะแสดงให้เห็นว่า
Emanuel Moecklin

ฉันใหม่ใน Android และฉันต้องการที่จะนั่งมากกว่านี้ onRequestPermissionsResult () วิธีการ แต่ฉันได้รับข้อผิดพลาดว่าต้องใช้วิธีการพิมพ์พิเศษ คุณสามารถบอกวิธีการใช้สิ่งนี้
Andrain

39

นี่เป็นวิธีที่ดีและง่ายในการตรวจสอบสถานะการอนุญาตปัจจุบัน:

    @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;
    }

Caveat: ส่งคืน BLOCKED_OR_NEVER_ASKED แอปเริ่มต้นครั้งแรกก่อนที่ผู้ใช้จะยอมรับ / ปฏิเสธการอนุญาตผ่านทางพรอมต์ผู้ใช้ (บนอุปกรณ์ sdk 23+)

ปรับปรุง:

ห้องสมุดสนับสนุน Android ในขณะนี้ดูเหมือนว่าจะมีระดับที่คล้ายกันมากandroid.support.v4.content.PermissionCheckerซึ่งมีcheckSelfPermission()ผลตอบแทน:

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

1
สำหรับการเปิดตัวครั้งแรกฉันกำลังเก็บบูลีนในค่ากำหนดที่แชร์
Saeid Farivar

5
นี่จะส่งคืนเสมอBLOCKED_OR_NEVER_ASKEDหากยังไม่ได้รับอนุญาต
Saket

6
ใช่นั่นคือเหตุผลที่เรียกว่า "BLOCKED_OR_NEVER_ASKED" และเห็นประโยคสุดท้าย
Patrick Favre

3
android.content.pmแล้วกำหนดและPERMISSION_GRANTED = 0 PERMISSION_DENIED = -1อาจจะตั้งค่าBLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1หรืออะไร?
samis

ดูคำตอบของmVckด้านล่างเพื่อจัดการข้อแม้
samis

28

เมื่อผู้ใช้ทำเครื่องหมายว่า "ไม่ต้องถามอีก" คำถามจะไม่สามารถแสดงได้อีก แต่สามารถอธิบายให้ผู้ใช้ทราบได้ว่าเขาเคยปฏิเสธสิทธิ์และต้องให้สิทธิ์ในการตั้งค่า และอ้างอิงเขาไปยังการตั้งค่าด้วยรหัสต่อไปนี้:

@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;
}

ในการโยกย้ายไปยัง androidX คุณสามารถแทนที่ android.support.design.R ด้วย com.google.android.material.R
Ridha Rezzag

26

อาจมีประโยชน์สำหรับใครบางคน: -

สิ่งที่ฉันได้สังเกตเห็นคือถ้าเราตรวจสอบ shouldShowRequestPermissionRationale () ธงใน onRequestPermissionsResult () วิธีการโทรกลับก็แสดงให้เห็นเพียงสองรัฐ

สถานะ 1: - รับคืนจริง: - เมื่อใดก็ตามที่ผู้ใช้คลิกปฏิเสธสิทธิ์ (รวมถึงครั้งแรกมาก)

สถานะ 2: - ส่งคืนค่าเท็จ: - หากผู้ใช้เลือก“ ไม่ต้องถามอีก”

ลิงค์ตัวอย่างการทำงานโดยละเอียด


2
นี่เป็นวิธีที่ถูกต้องในการตรวจสอบว่าผู้ใช้ได้เลือกตัวเลือกไม่ต้องถามอีก
Muhammad Babar

อากุญแจที่นี่คือคุณจัดการเรื่องนี้ในonRequestPermissionsResultไม่ใช่เมื่อได้รับอนุญาต
Joshua Pinter

26

คุณสามารถตรวจสอบได้โดยการตรวจสอบว่าเหตุผลการอนุญาตจะแสดงในonRequestPermissionsResult()วิธีการโทรกลับหรือไม่ และหากคุณพบการอนุญาตใด ๆ ที่ตั้งไว้ว่าไม่ต้องถามอีกคุณสามารถขอให้ผู้ใช้อนุญาตจากการตั้งค่า

การใช้งานเต็มรูปแบบของฉันจะเป็นด้านล่าง มันทำงานได้ทั้งการร้องขอสิทธิ์เดียวหรือหลาย ใช้สิ่งต่อไปนี้หรือใช้ห้องสมุดของฉันโดยตรง

@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 ความต้องการของฉันคือเมื่อฉันคลิกที่ปุ่มดาวน์โหลด (ซึ่งดาวน์โหลดไฟล์ pdf) ในเวลานั้นต้องตรวจสอบสิทธิ์การเขียนอนุญาตหรือปฏิเสธดังนั้นวิธีการใช้รหัสนี้! คุณช่วยแนะนำฉันได้ไหม
Rucha Bhatt Joshi

สวัสดี @RuchaBhatt ดูห้องสมุดของฉัน github.com/nabinbhandari/Android-Permissions
Nabin Bhandari

15

หากคุณต้องการตรวจพบ "สถานะ" ทั้งหมด (ถูกปฏิเสธครั้งแรกเพิ่งถูกปฏิเสธเพิ่งถูกปฏิเสธด้วย "ไม่ต้องถามอีก" หรือถูกปฏิเสธอย่างถาวร) คุณสามารถทำสิ่งต่อไปนี้:

สร้าง 2 บูลีน

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

ตั้งค่าแรกก่อนขออนุญาต:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

ตั้งค่าอันที่สองภายในเมธอด onRequestPermissionsResult ของคุณ:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

ใช้ "ตาราง" ต่อไปนี้เพื่อทำสิ่งที่คุณต้องการใน onRequestPermissionsResult () (หลังจากตรวจสอบว่าคุณยังไม่มีสิทธิ์):

// 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

ไม่มีจุดใดในการตรวจสอบ shouldShowRequestPermissionRationale ก่อนที่จะโทรไปยัง requestPermissions เว้นแต่คุณต้องการแสดงเหตุผลก่อนที่จะขออนุญาต แสดงเหตุผลเฉพาะหลังจากที่ผู้ใช้ปฏิเสธการอนุญาตดูเหมือนว่าแอพส่วนใหญ่จัดการกับมันในทุกวันนี้
Emanuel Moecklin

2
@EmanuelMoecklin เท่าที่ฉันรู้ว่ามันเป็นวิธีเดียวที่จะตรวจสอบว่ามันถูกปฏิเสธ (โดยตรวจสอบก่อนและหลังตามที่อธิบายไว้ในตารางความจริงของฉัน) หรือถ้ามันเป็นครั้งแรกที่ปฏิเสธ (ในกรณีของฉันฉันเปลี่ยนเส้นทางผู้ใช้ไป การตั้งค่าแอพหากถูกปฏิเสธอย่างถาวร)
mVck

1
// TRUE FALSEยังเกิดขึ้นเมื่อผู้ใช้อนุญาตการอนุญาตหลังจากที่ก่อนหน้านี้ปฏิเสธ
samis

11

ฉันมีปัญหาเดียวกันและคิดออก เพื่อให้ชีวิตง่ายขึ้นฉันเขียน class util เพื่อจัดการสิทธิ์ใช้งานจริง

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();
    }
}

และวิธีการตั้งค่าใช้งานมีดังนี้

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);
}

ตอนนี้สิ่งที่คุณต้องทำก็คือใช้เมธอด * checkPermission * พร้อมอาร์กิวเมนต์ที่เหมาะสม

นี่คือตัวอย่าง

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();
                        }
                    });

แอพของฉันจะรู้ได้อย่างไรว่าผู้ใช้ตรวจสอบ "ไม่ต้องถามอีก"

หากผู้ตรวจสอบไม่เคยถามอีกคุณจะได้รับการติดต่อกลับในonPermissionDisabled

Happy coding :)


shouldShowRequestPermissionRationale ฉันพบข้อผิดพลาดที่นี่คุณช่วยฉันได้ไหม
Rucha Bhatt Joshi

ฉันไม่สามารถหาวิธีนี้ควรแสดงให้เห็นว่าอาจจะเป็นเพราะฉันล้มเหลวในการรับบริบท .. แต่ก็ดีฉันพบวิธีอื่นทางเลือก .. ขอบคุณสำหรับความช่วยเหลือ :)
Rucha Bhatt Joshi

1
ความผิดฉันเอง. ควรมี ShowRequestPermissionRationale ผ่านทางกิจกรรมไม่ใช่บริบท ฉันอัปเดตคำตอบของฉันโดยส่งบริบทเป็นกิจกรรมก่อนเรียกวิธีการนั้น ลองดู :)
muthuraj

1
นี่เป็นวิธีเดียวที่จะได้รับค่าเท็จแรกที่ส่งคืนโดยshouldShowRequestPermissionRationaleบันทึกการตั้งค่าคำขอที่ส่งไปยังผู้ใช้ ฉันมีความคิดเดียวกันและพบคำตอบของคุณ Nice job man
MatPag

4

คำอธิบายที่สมบูรณ์สำหรับทุกกรณีที่ได้รับอนุญาต

/**
 *    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

ฟังก์ชั่นที่มีประโยชน์เพื่อตรวจสอบว่าการอนุญาตโดยพลการถูกบล็อกจากการร้องขอ (ใน 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
}

การใช้สิ่งนี้ต้องการการตั้งค่าบูลีนที่ใช้ร่วมกันที่มีชื่อของสิทธิ์ที่คุณต้องการ (เช่นandroid.Manifest.permission.READ_PHONE_STATE) trueเมื่อคุณขออนุญาตครั้งแรก


คำอธิบาย:

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M เนื่องจากบางส่วนของรหัสนั้นสามารถเรียกใช้บน API ระดับ 23+ เท่านั้น

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED เพื่อตรวจสอบว่าเราไม่ได้รับอนุญาต

!activity.shouldShowRequestPermissionRationale(permission)เพื่อตรวจสอบว่าผู้ใช้ปฏิเสธแอปถามอีกครั้งหรือไม่ เนื่องจากคุณสมบัติของฟังก์ชันนี้จึงจำเป็นต้องมีบรรทัดต่อไปนี้

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) สิ่งนี้ถูกใช้ (พร้อมกับการตั้งค่าเป็นจริงในคำขอการอนุญาตครั้งแรก) เพื่อแยกความแตกต่างระหว่างสถานะ "ไม่เคยถาม" และ "ไม่ต้องถามอีก" เนื่องจากบรรทัดก่อนหน้านี้จะไม่ส่งคืนข้อมูลนี้


4

วิธีการshouldShowRequestPermissionRationale ()สามารถใช้เพื่อตรวจสอบว่าผู้ใช้เลือกตัวเลือก 'ไม่เคยถามอีกแล้ว' และปฏิเสธการอนุญาต มีตัวอย่างโค้ดมากมายดังนั้นฉันอยากจะอธิบายวิธีใช้มันเพื่อจุดประสงค์ดังกล่าวเพราะฉันคิดว่าชื่อและการนำไปปฏิบัติทำให้มันซับซ้อนกว่าที่เป็นจริง

ตามที่อธิบายไว้ในการร้องขอสิทธิ์ ณ รันไทม์เมธอดนั้นจะคืนค่าจริงถ้าตัวเลือก 'ไม่ถามอีก' จะปรากฏเป็นเท็จมิฉะนั้น ดังนั้นจึงส่งกลับค่าเท็จในครั้งแรกที่มีการแสดงกล่องโต้ตอบจากนั้นเป็นครั้งที่สองเมื่อมันกลับมาจริงและเฉพาะในกรณีที่ผู้ใช้ปฏิเสธการอนุญาตเลือกตัวเลือก ณ จุดนั้นมันกลับเท็จอีกครั้ง

ในการตรวจจับกรณีเช่นนี้คุณสามารถตรวจจับลำดับ false-true-false หรือ (ง่ายขึ้น) คุณสามารถมีแฟล็กที่คอยติดตามเวลาเริ่มต้นที่ไดอะล็อกแสดง หลังจากนั้นวิธีการนั้นจะคืนค่าเป็น true หรือ false โดยที่ false จะอนุญาตให้คุณตรวจจับเมื่อเลือกตัวเลือก


3

กรุณาอย่าขว้างก้อนหินใส่ฉันเพื่อแก้ปัญหานี้

ใช้งานได้ แต่เป็น "แฮ็ค" เล็กน้อย

เมื่อคุณโทรติดต่อrequestPermissionsลงทะเบียนเวลาปัจจุบัน

        mAskedPermissionTime = System.currentTimeMillis();

จากนั้นใน onRequestPermissionsResult

หากไม่ได้รับผลตรวจสอบเวลาอีกครั้ง

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

เนื่องจากผู้ใช้ไม่สามารถคลิกอย่างรวดเร็วบนปุ่มปฏิเสธเรารู้ว่าเขาเลือก "ไม่ต้องถามอีก" เพราะการโทรกลับเป็นแบบทันที

ใช้ความเสี่ยงของคุณเอง


ถ้าเราเห็นกล่องโต้ตอบที่ร้องขอเป็นเวลา 5 นาทีแล้วปฏิเสธ
saksham

แล้วการใช้สิ่งนี้คืออะไรถ้ามันไม่สามารถตอบสนองความต้องการขั้นพื้นฐาน รหัสอาจเป็นแฮ็คที่ยอมรับหากเห็นได้ชัดเจนว่าตอบสนองความต้องการทั้งหมดในทุกกรณีไม่ได้
saksham

ใช่สิ่งนี้ไม่ดี ผู้ทดสอบอัตโนมัติเช่นนี้อาจสามารถคลิกได้เร็วกว่านั้น: developer.android.com/training/testing/crawler
stackzebra

2

ฉันเขียนชวเลขสำหรับขออนุญาตใน Android M. รหัสนี้ยังจัดการความเข้ากันได้ย้อนหลังกับ Android รุ่นเก่า

รหัสที่น่าเกลียดทั้งหมดจะถูกแยกออกเป็นส่วนที่แนบและแยกตัวเองไปยังกิจกรรมที่ร้องขอการอนุญาตคุณสามารถใช้PermissionRequestManagerดังต่อไปนี้:

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();

ดู: 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

ลองไลบรารี่ที่ได้รับอนุญาตง่ายๆนี้ มันจะจัดการการดำเนินงานทั้งหมดที่เกี่ยวข้องกับการอนุญาตใน 3 ขั้นตอนง่าย ๆ มันช่วยประหยัดเวลาของฉัน คุณสามารถเสร็จสิ้นการทำงานทั้งหมดได้รับอนุญาตที่เกี่ยวข้องใน 15 นาที

มันสามารถจัดการ Deny, มันสามารถจัดการไม่ต้องถามอีก, สามารถเรียกการตั้งค่าแอพเพื่อขออนุญาต, มันสามารถให้ข้อความ Rational, มันสามารถให้ข้อความ Denial, มันสามารถให้รายการของการอนุญาตที่ยอมรับได้, มันสามารถให้รายการปฏิเสธ สิทธิ์และอื่น ๆ

https://github.com/ParkSangGwon/TedPermission

ขั้นตอนที่ 1:เพิ่มการพึ่งพาของคุณ

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

ขั้นตอนที่ 2:ถามสิทธิ์

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();

ขั้นตอนที่ 3:จัดการการตอบสนองการอนุญาต

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();
    }
};

ยิ่งใหญ่ มันช่วยประหยัดเวลาของฉัน
Vigneswaran A

ดีใช้งานง่าย
Uray Febri

2

คุณสามารถฟังสวย

ผู้ฟัง

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

MainClass เพื่อขออนุญาต

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()
        }

    }
}

ใช้วิธีนี้

      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")
                    }

                })

แทนที่ onRequestPermissionsResult ในกิจกรรมหรือ 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

แต่คุณจะได้รับการโทรกลับonRequestPermissionsResult()เป็น PERMISSION_DENIED เมื่อคุณขออนุญาตอีกครั้งในขณะที่อยู่ในสภาพที่ไม่ถูกต้องshouldShowRequestPermissionRationale()

จาก Android doc:

เมื่อระบบขอให้ผู้ใช้ให้อนุญาตผู้ใช้มีตัวเลือกในการบอกให้ระบบไม่ขออนุญาตนั้นอีกครั้ง ในกรณีดังกล่าวทุกครั้งที่แอปใช้requestPermissions()เพื่อขอการอนุญาตนั้นอีกครั้งระบบจะปฏิเสธคำขอทันที ระบบเรียกonRequestPermissionsResult()ใช้วิธีการโทรกลับของคุณและผ่านPERMISSION_DENIEDเช่นเดียวกับที่ผู้ใช้ปฏิเสธคำขอของคุณอีกครั้งอย่างชัดเจน ซึ่งหมายความว่าเมื่อคุณโทรrequestPermissions()คุณไม่สามารถสันนิษฐานได้ว่าการโต้ตอบโดยตรงกับผู้ใช้เกิดขึ้น


1

คุณสามารถใช้ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)วิธีการตรวจสอบว่าไม่มีการถามการตรวจสอบหรือไม่

สำหรับการอ้างอิงเพิ่มเติม: ตรวจสอบสิ่งนี้

วิธีตรวจสอบการอนุญาตหลายรายการ:

  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.
                        }

อธิบายวิธี ()

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();
    }

โค้ดด้านบนจะแสดงข้อความโต้ตอบซึ่งจะเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าจอการตั้งค่าแอพจากที่ซึ่งเขาสามารถให้สิทธิ์หากตรวจสอบว่าไม่เคยถามปุ่มอีกเลย


1

คุณสามารถใช้ได้

shouldShowRequestPermissionRationale()

ภายใน

onRequestPermissionsResult()

ดูตัวอย่างด้านล่าง:

ตรวจสอบว่ามีสิทธิ์เมื่อผู้ใช้คลิกที่ปุ่ม:

@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);
            }
        } 
    }
}

เมื่อผู้ใช้ตอบกล่องโต้ตอบการอนุญาตเราจะไปที่ 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

ฉันต้องการรับข้อมูลด้วยหรือไม่ว่าผู้ใช้เลือก "ไม่ต้องถามอีก" ฉันได้รับ 'เกือบแก้ปัญหา' ด้วยธงดูน่าเกลียด แต่ก่อนที่ฉันจะบอกคุณว่าฉันจะบอกคุณเกี่ยวกับแรงจูงใจของฉัน:

ฉันต้องการเสนอฟังก์ชั่นอ้างอิงการอนุญาตในขั้นต้น หากผู้ใช้ใช้และไม่มีสิทธิ์เขา / เธอจะได้รับการโต้ตอบที่ 1 จากด้านบนหรือทั้งที่ 2 และ 3 เมื่อผู้ใช้เลือก 'ไม่ต้องถามอีก' ฉันต้องการปิดการใช้งานและแสดงต่างกัน - การกระทำของฉันถูกเรียกใช้โดยรายการข้อความสปินเนอร์ฉันต้องการเพิ่ม '(เพิกถอนสิทธิ์) ไปยังข้อความที่แสดง สิ่งนี้แสดงต่อผู้ใช้: 'มีฟังก์ชั่นการใช้งาน แต่ฉันไม่สามารถใช้งานได้เนื่องจากการตั้งค่าสิทธิ์ของฉัน' อย่างไรก็ตามดูเหมือนว่าจะเป็นไปไม่ได้เนื่องจากฉันไม่สามารถตรวจสอบว่าได้เลือกตัวเลือก 'ไม่ต้องถามอีก' หรือไม่

ฉันมาหาวิธีแก้ปัญหาที่ฉันสามารถใช้ชีวิตด้วยการเปิดใช้งานฟังก์ชั่นของฉันเสมอพร้อมการตรวจสอบสิทธิ์ที่ใช้งานอยู่ ฉันกำลังแสดงข้อความ Toast ใน onRequestPermissionsResult () ในกรณีของการตอบสนองเชิงลบ แต่เฉพาะในกรณีที่ฉันไม่ได้แสดงป๊อปอัพเหตุผลที่กำหนดเองของฉัน ดังนั้นหากผู้ใช้เลือก 'ไม่ต้องถามอีก' เขาจะได้รับข้อความอวยพรเท่านั้น หากผู้ใช้ลังเลที่จะเลือก 'ไม่ต้องถามอีก' เขาจะได้รับเฉพาะเหตุผลที่กำหนดเองและป๊อปอัพคำขออนุญาตโดยระบบปฏิบัติการ แต่ไม่ใช่ขนมปังปิ้งเนื่องจากการแจ้งเตือนสามครั้งในแถวจะเจ็บปวดมากเกินไป


0

ฉันต้องใช้การอนุญาตแบบไดนามิกสำหรับกล้องถ่ายรูป ในกรณีที่เป็นไปได้ 3 กรณี: 1. อนุญาต, 2. ปฏิเสธ, 3. อย่าถามอีก

 @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

การขยายคำตอบของmVckข้างต้นตรรกะต่อไปนี้จะกำหนดว่า "ไม่ต้องถามอีก" ได้รับการตรวจสอบคำขอการอนุญาตที่กำหนด:

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

ซึ่งคัดลอกมาจากด้านล่าง (สำหรับตัวอย่างเต็มดูคำตอบนี้)

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

คุณสามารถอ่านเอกสารอย่างเป็นทางการของ Android เพื่อ ขออนุญาตการใช้แอพ

หรือคุณสามารถค้นหาไลบรารี่ที่ได้รับความนิยมมากมายของ Github


2
เพียงแค่แนะนำห้องสมุดไม่ใช่ anwer ดี: อย่างน้อยอธิบายวิธีใช้พวกเขาในการแก้ปัญหา
M. Prokhorov

0

เพื่อตอบคำถามอย่างแม่นยำจะเกิดอะไรขึ้นเมื่อผู้ใช้กด "ไม่ต้องถามอีก"

วิธีการ / ฟังก์ชั่นแทนที่

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

อาร์เรย์ grantResult ออกมาว่างเปล่าดังนั้นคุณสามารถทำอะไรที่นั่นได้บ้าง? แต่ไม่ใช่วิธีปฏิบัติที่ดีที่สุด

วิธีจัดการกับ "ไม่ต้องถามอีก"

ฉันทำงานกับ Fragment ซึ่งต้องได้รับอนุญาต 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()

            }
        }
    }

ฟังก์ชั่นอื่น ๆ ไม่สำคัญ

// 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)
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.