Android M 권한 : shouldShowRequestPermissionRationale () 함수 사용에 혼란
Android M의 새로운 권한 모델에 대한 공식 문서를 살펴 보았습니다 . 앱이 이전에이 권한을 요청하고 사용자가 요청을 거부 한 경우 shouldShowRequestPermissionRationale()
반환 되는 함수 에 대해 설명합니다 true
. 사용자가 과거에 권한 요청을 거절하고 다시 묻지 않음 옵션을 선택한 경우이 메소드는를 반환합니다 false
.
그러나 다음 두 경우를 어떻게 구별 할 수 있습니까?
사례 1 : 앱에 권한이 없으며 사용자에게 이전에 권한을 요청하지 않았습니다. 이 경우 shouldShowRequestPermissionRationale ()은 사용자에게 처음 요청하기 때문에 false를 반환합니다.
사례 2 : 사용자가 권한을 거부하고 "다시 묻지 않음"을 선택했습니다.이 경우 역시 showShowPermissionPermissionRationale ()이 false를 반환합니다.
Case 2의 앱 설정 페이지로 사용자를 보내려고합니다.이 두 경우를 어떻게 구분합니까?
M Preview 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... }
에서 권한이 거부되었거나 부여되었는지 확인하십시오
onRequestPermissionsResult
.이전에 권한이 거부 된 경우 권한 대화 상자에 다시 묻지 않음 확인란 이 있습니다 .
shouldShowRequestPermissionRationale
사용자가 다시 묻지 않음을 선택했는지 확인하기 위해 전화 를 겁니다.shouldShowRequestPermissionRationale
사용자가 다시 묻지 않음을 선택 했거나 기기 정책에 따라 앱에 해당 권한이 없는 경우 메소드는 false를 반환합니다 .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... } } }
따라서 사용자가 다시 묻지 않음을 선택했는지 여부를 추적 할 필요가 없습니다.
나는 같은 문제가 있었고 그것을 알아 냈습니다. 인생을 훨씬 간단하게 만들기 위해 런타임 권한을 처리하는 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();
}
}
그리고 PreferenceUtil의 방법은 다음이다.
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 : 앱에 권한이 없으며 사용자에게 이전에 권한을 요청하지 않았습니다. 이 경우 shouldShowRequestPermissionRationale ()은 사용자에게 처음 요청하기 때문에 false를 반환합니다.
사례 2 : 사용자가 권한을 거부하고 "다시 묻지 않음"을 선택했습니다.이 경우 역시 showShowPermissionPermissionRationale ()이 false를 반환합니다.
Case 2의 앱 설정 페이지로 사용자를 보내려고합니다.이 두 경우를 어떻게 구분합니까?
사례 1의 경우 onPermissionAsk , 사례 2의 경우 onPermissionDisabled에 대한 콜백을 받습니다 .
행복한 코딩 :)
최신 정보
아래 의 CanC의 답변 이 따라야 할 올바른 답변 이라고 생각합니다 . 확실하게 알 수있는 유일한 방법은 shouldShowPermissionRationale을 사용하여 onRequestPermissionResult 콜백에서이를 확인하는 것입니다.
==
내 원래 답변 :
내가 찾은 유일한 방법은 이것이 처음인지 아닌지 스스로 추적하는 것입니다 (예 : 공유 환경 설정 사용). 처음이 아니라면 shouldShowRequestPermissionRationale()
차별화 를 위해 사용하십시오 .
Android M-런타임 권한 확인-사용자가 "다시 묻지 않음"을 확인했는지 확인하는 방법 도 참조하십시오.
내가 이해하는 shouldShowRequestPermissionRationale ()은 후드 아래에서 많은 유스 케이스를 실행하고 요청되는 권한에 대한 설명을 표시할지 여부를 앱에 알립니다.
런타임 권한의 기본 개념은 대부분 사용자가 권한 요청에 대해 예라고 대답한다는 것입니다. 그렇게하면 사용자는 한 번의 클릭 만하면됩니다. 물론 요청은 올바른 컨텍스트에서 사용되어야합니다. 즉 "카메라"버튼을 누를 때 카메라 권한을 요청합니다.
사용자가 요청을 거부했지만 시간이 지나고 "카메라"버튼을 다시 누르면 shouldShowRequestPermissionRationale ()이 true를 반환하므로 앱에서 권한이 요청 된 이유와 앱이 실패한 이유를 의미있는 설명으로 표시 할 수 있습니다. 그것없이 제대로 작동합니다. 일반적으로 해당 대화 상자 창에는 다시 거부 / 나중에 결정하는 버튼과 권한을 부여하는 버튼이 표시됩니다. 근거 대화 상자의 권한 부여 버튼은 권한 요청을 다시 시작해야합니다. 이번에는 사용자에게 "다시 표시 안 함"확인란이 있습니다. 그가 선택하고 권한을 다시 거부하기로 결정하면 사용자와 앱이 같은 페이지에 있지 않다는 것을 Android 시스템에 알립니다. 이 조치는 두 가지 결과를 초래합니다. shouldShowRequestPermissionRationale ()은 항상 false를 리턴합니다.
그러나 onRequestPermissionsResult를 사용할 수있는 또 다른 가능한 시나리오가 있습니다. 예를 들어 일부 장치에는 카메라를 비활성화하는 장치 정책이있을 수 있습니다 (CIA, DARPA 등에서 작동). 이러한 장치에서 onRequestPermissionsResult는 항상 false를 반환하며 requestPermissions () 메서드는 자동으로 요청을 거부합니다.
Android 프레임 워크의 제품 관리자 인 Ben Poiesz와 함께 팟 캐스트를 들으면서 수집 한 것입니다.
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html
누군가가 기분이 좋으면 다른 옵션을 게시하십시오. "Android M 시스템 권한 단순화"에 대해 Google 자체에서 제공 한 EasyPermissions 를 사용할 수 있습니다 .
그런 다음 shouldShowRequestPermissionRationale
직접 처리 할 필요가 없습니다 .
이 구현을 확인하십시오. 나를 위해 아주 잘 작동합니다. 기본적으로 checkPermissions () 메서드에서 권한 목록을 전달하여 권한을 확인합니다. onRequestPermissionsResult ()에서 권한 요청 결과를 확인하십시오. 구현은 사용자가 "다시 묻지 않음"을 선택할 때 두 가지 경우 모두를 해결하도록합니다. 이 구현에서 se가 "다시 묻지 않음"을 선택하면 대화 상자에 앱 설정 활동으로 이동하는 옵션이 있습니다.
이 모든 코드는 내 조각 안에 있습니다. 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);
}
누군가에게 유용 할 수 있습니다 :-
내가 주목 한 것은 onRequestPermissionsResult () 콜백 메소드에 shouldShowRequestPermissionRationale () 플래그를 확인하면 두 가지 상태 만 표시한다는 것입니다.
상태 1 : -Return true :-사용자가 권한 거부를 클릭 할 때마다 (처음 포함).
상태 2 :-거짓을 반환합니다.-사용자가 "다시 묻지 않습니다"를 선택한 경우.
이런 식으로 할 수 있습니까?
@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;
}
}
}
누군가 Kotlin 솔루션에 관심이 있다면 @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)
}
}
}
shouldShowRequestPermissionRationale
SPECIAL 권한의 경우 사용자가 확인란없이 거부 한 후에 만 항상 TRUE를 반환합니다.
우리는 거짓 가치에 관심이 있습니다
따라서 잘못된 값으로 3 가지 경우가 손실됩니다 .
1. 이전에는 그러한 조치가 없었으며 현재 사용자는 동의 또는 거부하기로 결정했습니다.
단순히 환경 정의 ASKED_PERMISSION_*
지금은 존재하지 않고 것 사실 에서 onRequestPermissionsResult
그 어떤 경우 Start의 동의 또는 거부
따라서이 환경 설정이 존재하지 않지만 확인할 이유 가 없습니다.shouldShowRequestPermissionRationale
2. 사용자가 동의를 클릭했습니다.
간단히 :
checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
어느 것이 true 를 반환 하고 확인할 이유 가 없습니다.shouldShowRequestPermissionRationale
3. 사용자가 확인란을 사용하여 거부를 클릭했습니다 (두 번 이상 요청)
그것은이다 시간 과 일에 shouldShowRequestPermissionRationale
있는 반환합니다 FALSE
(우선 순위가 있으며 권한이 없습니다)
이 코드는 런타임 중에 사용자에게 권한을 요청하고 사용자가 허용하면 결과 메소드를 실행합니다. 사용자가 거부하면 사용자 거부로 설명을 다시 요청합니다 (명령으로 다시 묻습니다). 다시 묻지 않고 지시와 함께 열린 설정 옵션을 표시합니다.
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;
}
}
}
'development' 카테고리의 다른 글
LaTeX : 텍스트 범위에서 줄 바꿈 방지 (0) | 2020.06.28 |
---|---|
Java 람다는 둘 이상의 매개 변수를 가질 수 있습니까? (0) | 2020.06.27 |
Chrome을 사용하여 요소에 바인딩 된 이벤트를 찾는 방법 (0) | 2020.06.27 |
대소 문자를 구분하지 않는 'in'-Python (0) | 2020.06.27 |
Chrome과 IE가 서버로 보내는 User-Agent에“Mozilla 5.0”을 넣는 이유는 무엇입니까? (0) | 2020.06.27 |