สิทธิ์ Android M: สร้างความสับสนในการใช้ฟังก์ชัน shouldShowRequestPermissionRationale ()


148

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

แต่เราจะแยกความแตกต่างระหว่างสองกรณีต่อไปนี้ได้อย่างไร

กรณีที่ 1 : แอพไม่ได้รับอนุญาตและผู้ใช้ไม่ได้รับอนุญาตก่อน ในกรณีนี้ควร ShowShowRequestPermissionRationale () จะกลับเท็จเพราะนี่เป็นครั้งแรกที่เราถามผู้ใช้

กรณีที่ 2 : ผู้ใช้ปฏิเสธการอนุญาตและเลือก "ไม่ต้องถามอีก" ในกรณีนี้ก็ควร ShowRequestPermissionRationale () จะกลับมาเป็นเท็จ

ฉันต้องการส่งผู้ใช้ไปยังหน้าการตั้งค่าของแอพในกรณีที่ 2 ฉันจะแยกความแตกต่างระหว่างสองกรณีนี้ได้อย่างไร


1
คำตอบที่ยอมรับได้ดี เป็นอีกทางเลือกหนึ่งที่คุณสามารถใช้การแชร์ล่วงหน้าเพื่อทราบว่าแอพได้ขออนุญาตมาก่อนหรือไม่ เพียงแค่โยนมันออกไปในกรณีที่มันใช้ได้กับสถานการณ์ของคนอื่น
Rockin4Life33

4
มีกรณีที่ 3 เช่นกัน: ผู้ใช้ถูกขอและอนุญาต / ปฏิเสธการอนุญาต แต่ใช้การตั้งค่าการอนุญาตเพื่อย้อนกลับไปที่ "ถามทุกครั้ง" การทดสอบแสดงให้เห็นว่าshouldShowRequestPermissionRationale()คืนค่าเท็จในกรณีนี้ซึ่งจะทำให้รหัสใด ๆ ที่อาศัยรหัส "ฉันถามก่อน" ซึ่งเป็นอันตราย
รถกระบะ Logan

นี่คือตัวอย่างของ Google ที่แสดงแนวทางปฏิบัติที่ดีที่สุดในpermissionsAndroid github.com/android/permissions-samples
itabdullah

@itabdullah โค้ดตัวอย่างของ Google ไร้ประโยชน์เนื่องจากพวกเขาไม่ได้พิจารณาการใช้ usecase ที่เป็นไปได้สูงของ "ผู้ใช้ไม่อนุญาตการอนุญาตครั้งล่าสุด" : - / ทั่วไป
บางคนที่ไหนสักแห่ง

คำตอบ:


172

หลังจาก M Preview 1 หากไดอะล็อกปรากฏขึ้นเป็นครั้งแรกจะไม่มีช่องทำเครื่องหมายไม่ต้องถามอีกเลย

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

ดังนั้นตรรกะควรเป็นดังนี้:

  1. ขออนุญาต:

    if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
    } else {
        //Do the stuff that requires permission...
    }
  2. onRequestPermissionsResultตรวจสอบว่าได้รับอนุญาตถูกปฏิเสธหรือยอมรับใน

    หากสิทธิ์ถูกปฏิเสธก่อนหน้านี้คราวนี้จะมีช่องทำเครื่องหมายNever ask อีกครั้งในช่องโต้ตอบการอนุญาต

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

    if (grantResults.length > 0){
        if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //Do the stuff that requires permission...
        }else if (grantResults[0] == PackageManager.PERMISSION_DENIED){
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                //Show permission explanation dialog...
            }else{
                //Never ask again selected, or device policy prohibits the app from having that permission.
                //So, disable that feature, or fall back to another situation...
            }
        }
    }

ดังนั้นคุณจะไม่ต้องติดตามว่าผู้ใช้ที่เลือกไม่ต้องถามอีกหรือไม่


49
จุดหนึ่งของการชี้แจงควรShowRequestPermissionRationale () จะส่งกลับค่าเท็จถ้าผู้ใช้ไม่เคยถูกถามเพื่อขออนุญาต (เช่นครั้งแรกที่มีการเรียกใช้แอปพลิเคชัน) คุณจะไม่พบกรณีดังกล่าวหากคุณทำตามตรรกะของตัวอย่างที่ให้ไว้ แต่ถ้อยคำที่ต่ำกว่า 2 เป็นสิ่งที่ทำให้เข้าใจผิดเล็กน้อย
Ben

15
ฉันไม่แน่ใจดูเหมือนว่ามีข้อบกพร่อง เราควรทราบได้อย่างไรว่าเป็นครั้งแรกที่ผู้ใช้ถูกถาม ฉันต้องติดตามว่าผู้ใช้ได้รับคำถามหรือไม่และถ้าเขาทำฉันต้องกลับตรรกะ ไม่สมเหตุสมผลสำหรับฉัน
Daniel F

4
ฉันคิดว่ามันเป็นที่น่าสังเกตว่าที่คุณจะผ่านcontextในพารามิเตอร์ที่เป็นจริงจากประเภทActivityCompat.shouldShowRequestPermissionRationale(...) Activityอาจไม่ส่งผลกระทบต่อคุณทั้งหมด แต่ในกรณีของฉันมันไม่
aProperFox

7
ตรรกะหุ่นยนต์นี้โง่มาก! มันบังคับให้ฉันโทรหาshouldในการโทรกลับและบันทึกค่าตัวนับใน NVM เพียงเพื่อทราบว่าฉันต้องแจ้งให้คำขออีกครั้งในครั้งต่อไปที่แอปเปิดขึ้น! ... ว้าว (facepalm) ... มันยากเกินไปที่จะโทรเพียงครั้งเดียวที่ส่งคืนการแจงสถานะหรือไม่?
Shockwaver

2
ฉันคิดว่านี่เป็นความล้มเหลวครั้งใหญ่ของ Google เอกสารอย่างเป็นทางการระบุว่าควรจะเรียกใช้ShowRequestPermissionRationale () ก่อนตรวจสอบการอนุญาต (ดูที่developer.android.com/training/permissions/requesting#explain ) แต่คำตอบทั้งหมดใน StackOverflow เรียกมันใน onRequestPermissionResult () ผู้ใช้คลิก "ไม่ต้องถามอีก" หรือไม่
MilošČernilovský

22

ฉันมีปัญหาเดียวกันและคิดออก เพื่อให้ชีวิตง่ายขึ้นมากฉันจึงเขียนคลาส 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();
                        }
                    });

กรณีที่ 1: แอพไม่ได้รับอนุญาตและผู้ใช้ไม่ได้รับอนุญาตก่อน ในกรณีนี้ควรShowRequestPermissionRationale () จะคืนค่าเท็จเพราะนี่เป็นครั้งแรกที่เราถามผู้ใช้

กรณีที่ 2: ผู้ใช้ปฏิเสธการอนุญาตและเลือก "ไม่ต้องถามอีก" ในกรณีนี้ก็ควร ShowRequestPermissionRationale () จะกลับมาเป็นเท็จ

ฉันต้องการส่งผู้ใช้ไปยังหน้าการตั้งค่าของแอพในกรณีที่ 2 ฉันจะแยกความแตกต่างระหว่างสองกรณีนี้ได้อย่างไร

คุณจะได้รับการติดต่อกลับบนonPermissionAskสำหรับกรณีที่ 1 และonPermissionDisabledสำหรับกรณีที่ 2

Happy coding :)


พี่อธิบายที่ยอดเยี่ยม ทำตามขั้นตอนเดียวกันของคุณ :)
Sumit Jha

ฉันจะเติมอะไรสำหรับกิจกรรมนี้? public void onPermissionAsk() { ActivityCompat.requestPermissions( thisActivity, ... .
Mardymar

@Mardymar thisActivityไม่มีอะไรYourActivity.thisนอกจาก
muthuraj

1
วิธีจัดการหลายสิทธิ์และวิธีการรวมรหัสนี้ในส่วน
Taimur

ชนิดของการcontextที่คุณใช้? ไม่อยู่ในshouldShowRequestPermissionRationale(permission) android.content.Contextมันอยู่ใน ActivityCompat
Hilikus

8

UPDATE

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

==

คำตอบเดิมของฉัน:

วิธีเดียวที่ฉันได้พบคือการติดตามด้วยตัวคุณเองว่านี่เป็นครั้งแรกหรือไม่ (เช่นใช้การตั้งค่าที่ใช้ร่วมกัน) หากไม่ใช่ครั้งแรกให้ใช้shouldShowRequestPermissionRationale()เพื่อแยกความแตกต่าง

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


1
ใช่แม้ฉันเห็นด้วยว่าวิธีการของ CanC นั้นเป็นสิ่งที่ควรปฏิบัติตาม ฉันจะทำเครื่องหมายว่าเป็นคำตอบที่ยอมรับได้
akshayt23

6

วิธีที่ฉันเข้าใจควร shouldShowRequestPermissionRationale () ใช้งานกรณีการใช้งานจำนวนมากภายใต้ประทุนและแจ้งให้แอปทราบว่าจะแสดงคำอธิบายเกี่ยวกับการอนุญาตที่ร้องขอหรือไม่

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

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

แต่ยังมีอีกสถานการณ์ที่เป็นไปได้ที่สามารถใช้ onRequestPermissionsResult ตัวอย่างเช่นอุปกรณ์บางอย่างอาจมีนโยบายอุปกรณ์ที่ปิดใช้งานกล้อง (ทำงานกับ CIA, DARPA ฯลฯ ) บนอุปกรณ์เหล่านี้ onRequestPermissionsResult จะส่งคืนค่าเท็จเสมอและเมธอด requestPermissions () จะปฏิเสธคำขอในทันที

นั่นคือสิ่งที่ฉันรวบรวมโดยการฟังพอดคาสต์กับ Ben Poiesz ผู้จัดการผลิตภัณฑ์ในกรอบของ Android
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html


6

เพียงโพสต์ตัวเลือกอื่นหากใครก็ตามอาจรู้สึกว่า คุณสามารถใช้EasyPermissionsซึ่งจัดทำโดย Google เองเพื่อกล่าวว่า "ลดความซับซ้อนของการอนุญาตระบบ Android M"

จากนั้นคุณไม่ต้องจัดการshouldShowRequestPermissionRationaleโดยตรง


ทำไมฉันไม่เห็นโครงการนี้ prevoiusly :)
Vlad

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

4

หากใครสนใจวิธีแก้ปัญหา Kotlin ฉันได้รับ @ reuthored @muthuraj ให้เป็น Kotlin ยังทำให้มันทันสมัยขึ้นเล็กน้อยเพื่อให้มีบล็อกที่สมบูรณ์แทนที่จะเป็นผู้ฟัง

PermissionUtil

object PermissionUtil {
    private val PREFS_FILE_NAME = "preference"

    fun firstTimeAskingPermission(context: Context, permission: String, isFirstTime: Boolean) {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        sharedPreference.preferences.edit().putBoolean(permission,
                isFirstTime).apply()
    }

    fun isFirstTimeAskingPermission(context: Context, permission: String): Boolean {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        return sharedPreference.preferences.getBoolean(permission,
                true)
    }
}

PermissionHandler

enum class CheckPermissionResult {
    PermissionAsk,
    PermissionPreviouslyDenied,
    PermissionDisabled,
    PermissionGranted
}

typealias PermissionCheckCompletion = (CheckPermissionResult) -> Unit


object PermissionHandler {

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        return ContextCompat.checkSelfPermission(context,
                permission) != PackageManager.PERMISSION_GRANTED
    }

    fun checkPermission(context: Context, permission: String, completion: PermissionCheckCompletion) {
        // If permission is not granted
        if (shouldAskPermission(context, permission)) {
            //If permission denied previously
            if ((context as Activity).shouldShowRequestPermissionRationale(permission)) {
                completion(CheckPermissionResult.PermissionPreviouslyDenied)
            } else {
                // Permission denied or first time requested
                if (PermissionUtil.isFirstTimeAskingPermission(context,
                                permission)) {
                    PermissionUtil.firstTimeAskingPermission(context,
                            permission,
                            false)
                    completion(CheckPermissionResult.PermissionAsk)
                } else {
                    // Handle the feature without permission or ask user to manually allow permission
                    completion(CheckPermissionResult.PermissionDisabled)
                }
            }
        } else {
            completion(CheckPermissionResult.PermissionGranted)
        }
    }
}

การดำเนินงาน

PermissionHandler.checkPermission(activity,
                    Manifest.permission.CAMERA) { result ->
                when (result) {
                    CheckPermissionResult.PermissionGranted -> {
                        // openCamera()
                    }
                    CheckPermissionResult.PermissionDisabled -> {
                        // displayAlert(noPermissionAlert)
                    }
                    CheckPermissionResult.PermissionAsk -> {
                        // requestCameraPermissions()
                    }
                    CheckPermissionResult.PermissionPreviouslyDenied -> {
                        // displayAlert(permissionRequestAlert)
                    }
                }
            }

3

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

รหัสทั้งหมดนี้อยู่ในส่วนของฉัน ฉันคิดว่าน่าจะดีกว่าถ้าสร้างคลาสพิเศษเพื่อทำสิ่งนี้เช่น PermissionManager แต่ฉันไม่แน่ใจเกี่ยวกับเรื่องนี้

/**
     * responsible for checking if permissions are granted. In case permissions are not granted, the user will be requested and the method returns false. In case we have all permissions, the method return true.
     * The response of the request for the permissions is going to be handled in the onRequestPermissionsResult() method
     * @param permissions list of permissions to be checked if are granted onRequestPermissionsResult().
     * @param requestCode request code to identify this request in
     * @return true case we already have all permissions. false in case we had to prompt the user for it.
     */
    private boolean checkPermissions(List<String> permissions, int requestCode) {
        List<String> permissionsNotGranted = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(getActivity(), permission) != PackageManager.PERMISSION_GRANTED)
                permissionsNotGranted.add(permission);
        }

        //If there is any permission we don't have (it's going to be in permissionsNotGranted List) , we need to request.
        if (!permissionsNotGranted.isEmpty()) {
            requestPermissions(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()]), requestCode);
            return false;
        }
        return true;
    }

    /**
     * called after permissions are requested to the user. This is called always, either
     * has granted or not the permissions.
     * @param requestCode  int code used to identify the request made. Was passed as parameter in the
     *                     requestPermissions() call.
     * @param permissions  Array containing the permissions asked to the user.
     * @param grantResults Array containing the results of the permissions requested to the user.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case YOUR_REQUEST_CODE: {
                boolean anyPermissionDenied = false;
                boolean neverAskAgainSelected = false;
                // Check if any permission asked has been denied
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        anyPermissionDenied = true;
                        //check if user select "never ask again" when denying any permission
                        if (!shouldShowRequestPermissionRationale(permissions[i])) {
                            neverAskAgainSelected = true;
                        }
                    }
                }
                if (!anyPermissionDenied) {
                    // All Permissions asked were granted! Yey!
                    // DO YOUR STUFF
                } else {
                    // the user has just denied one or all of the permissions
                    // use this message to explain why he needs to grant these permissions in order to proceed
                    String message = "";
                    DialogInterface.OnClickListener listener = null;
                    if (neverAskAgainSelected) {
                        //This message is displayed after the user has checked never ask again checkbox.
                        message = getString(R.string.permission_denied_never_ask_again_dialog_message);
                        listener = new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //this will be executed if User clicks OK button. This is gonna take the user to the App Settings
                                startAppSettingsConfigActivity();
                            }
                        };
                    } else {
                        //This message is displayed while the user hasn't checked never ask again checkbox.
                        message = getString(R.string.permission_denied_dialog_message);
                    }
                    new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme)
                            .setMessage(message)
                            .setPositiveButton(getString(R.string.label_Ok), listener)
                            .setNegativeButton(getString(R.string.label_cancel), null)
                            .create()
                            .show();
                }
            }
            break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    /**
     * start the App Settings Activity so that the user can change
     * settings related to the application such as permissions.
     */
    private void startAppSettingsConfigActivity() {
        final Intent i = new Intent();
        i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        i.addCategory(Intent.CATEGORY_DEFAULT);
        i.setData(Uri.parse("package:" + getActivity().getPackageName()));
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        getActivity().startActivity(i);
    }

2

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

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

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

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

การเชื่อมโยงสำหรับตัวอย่างการทำงานรายละเอียด


6
มันกลับเท็จเป็นครั้งแรก ไม่เป็นความจริง
JoM

ใช่นั่นคือสิ่งที่ฉันพูดถึงถ้าคุณตรวจสอบการตั้งค่าสถานะในวิธีการโทรกลับ onRequestPermissionsResult () มันจะมีสองสถานะเท่านั้นโดยเฉพาะในการติดต่อกลับนี้
Nicks

2
น่าเสียดายที่ควรแสดงShowRequestPermissionRationaleคืนค่าเท็จเสมอไม่ว่าผู้ใช้จะปฏิเสธการอนุญาตหรือไม่ก็ตาม
IgorGanapolsky

1

เราสามารถทำได้ด้วยวิธีนี้

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

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

@PermissionStatus
public static int getPermissionStatus(Activity activity, String permission) {
    if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
        return DENIED;
    } else {
        if (ActivityCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED) {
            return GRANTED;
        } else {
            return NEVER;
        }
    }
}

น่าเสียดายที่รหัสนี้ไม่แยกความแตกต่างระหว่างสถานการณ์ที่ไม่เคยขออนุญาตมาก่อนและไม่ได้ตรวจสอบที่ "ไม่ขออีกครั้ง"
เบ็น

คุณควรใช้การรวมกันของคลาสนี้ + ผู้ช่วยการอนุญาตเพื่อตรวจสอบว่าได้รับอนุญาตหรือไม่
ดร. aNdRO

0

shouldShowRequestPermissionRationale สำหรับสิทธิ์พิเศษส่งคืน TRUE เท่านั้นหลังจากผู้ใช้ปฏิเสธโดยไม่มีช่องทำเครื่องหมาย

เราสนใจค่าFALSE

ดังนั้นจึงมี3กรณีที่หายไปด้วยค่าเท็จ :

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

เพียงแค่กำหนดการตั้งค่าASKED_PERMISSION_*ที่ไม่ได้อยู่ในขณะนี้และจะเป็นจริงในonRequestPermissionsResultที่มันเริ่มต้นในกรณีที่เห็นด้วยหรือปฏิเสธ

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

2.ผู้ใช้คลิกเห็นด้วย

เพียงทำ:

checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED

ซึ่งจะส่งคืนจริงและไม่มีเหตุผลในการตรวจสอบshouldShowRequestPermissionRationale

3.ผู้ใช้คลิกปฏิเสธด้วยช่องทำเครื่องหมาย (ถามครั้งที่สองหรือมากกว่า)

ถึงเวลาทำงานแล้วshouldShowRequestPermissionRationaleซึ่งจะคืนค่าเป็นเท็จ

(มีการตั้งค่าอยู่และเราไม่ได้รับอนุญาต)


0

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

public String storagePermissions = Manifest.permission.READ_EXTERNAL_STORAGE;   
private static final int REQUEST_ACCESS =101;  

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      if(checkSelfPermission(storagePermissions)== PackageManager.PERMISSION_GRANTED){
          result();    // result  is your block of code 
      }else {
          requestPermissions(new String[]{storagePermissions},REQUEST_ACCESS);
      }

    }
    else{
        result();    //so if user is lower than api verison M, no permission is requested
    } 

}

 private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
    new AlertDialog.Builder(MainActivity.this)
            .setMessage(message)
            .setTitle("Hi User..")
            .setPositiveButton("Ok", okListener)
            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {        //idea calling showMessage funtion again
                    Snackbar mySnackbar = Snackbar.make( findViewById(R.id.coordinatorlayout),"You Press Cancel.. ", Snackbar.LENGTH_INDEFINITE);
                    mySnackbar.setAction("Exit", new cancelButton());
                    mySnackbar.show();

                }
            })
            .create()
            .show();
}


private void result(){
          //your code
}

    @RequiresApi(api = Build.VERSION_CODES.M)
public class NeverAskAgain implements View.OnClickListener{
    @Override
    public void onClick(View view)
    {
        goToSettings();
    }
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void goToSettings() {
    Intent myAppSettings = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
    finish();
    myAppSettings.addCategory(Intent.CATEGORY_DEFAULT);
    myAppSettings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivityForResult(myAppSettings, REQUEST_APP_SETTINGS);
}
public class cancelButton implements View.OnClickListener{
    @Override
    public void onClick(View view){
        Toast.makeText(MainActivity.this,"To use this app , you must grant storage permission",Toast.LENGTH_SHORT);
        finish();
    }
    }


 @Override
@RequiresApi(api = Build.VERSION_CODES.M)
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode,permissions,grantResults);

    switch(requestCode) {
        case REQUEST_ACCESS:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission is granted
                    result();
                    break;
                }
                else if (!shouldShowRequestPermissionRationale(permissions[0])){
                    showMessageOKCancel("You choose Never Ask Again,option",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Snackbar mySnackbar = Snackbar.make(findViewById(R.id.coordinatorlayout), "Permission=>Storage=>On", Snackbar.LENGTH_INDEFINITE);
                        mySnackbar.setAction("Settings", new NeverAskAgain());
                        mySnackbar.show();
                    }
                     });
                    break;
                }
                else {
                    showMessageOKCancel("You Denid permission Request..",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            requestPermissions(new String[]{storagePermissions}, REQUEST_ACCESS);
                        }
                    });
                    break;
                }
        }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.