Android M-Berechtigungen: Verwirrt bei der Verwendung der Funktion shouldShowRequestPermissionRationale()
Lesezeit: 14 Minuten
Ich habe das offizielle Dokument über das neue Berechtigungsmodell in Android M durchgesehen shouldShowRequestPermissionRationale() Funktion, die zurückkehrt true wenn die App diese Berechtigung zuvor angefordert hat und der Benutzer die Anfrage abgelehnt hat. Wenn der Benutzer die Berechtigungsanfrage in der Vergangenheit abgelehnt und die Option Nicht erneut fragen ausgewählt hat, kehrt diese Methode zurück false.
Aber wie können wir zwischen den folgenden beiden Fällen unterscheiden?
Fall 1: Die App hat keine Berechtigung und der Benutzer wurde vorher nicht um die Berechtigung gebeten. In diesem Fall gibt shouldShowRequestPermissionRationale() false zurück, da dies das erste Mal ist, dass wir den Benutzer fragen.
Fall 2: Der Benutzer hat die Berechtigung verweigert und “Nicht erneut fragen” ausgewählt, auch in diesem Fall wird shouldShowRequestPermissionRationale() false zurückgeben.
Ich möchte den Benutzer in Fall 2 auf die Einstellungsseite der App schicken. Wie unterscheide ich diese beiden Fälle?
Die akzeptierte Antwort ist gut. Alternativ können Sie auch eine gemeinsame Voreinstellung verwenden, um zu wissen, ob die App die Erlaubnis zuvor angefordert hat. Wirf das einfach raus, falls es für die Situation eines anderen besser geeignet ist.
– Rockin4Life33
7. Mai 16 um 18:47 Uhr
Es gibt auch Fall 3: Der Benutzer wurde nach der Berechtigung gefragt und diese erteilt/verweigert, aber er hat die Berechtigungseinstellungen verwendet, um wieder auf „jedes Mal nachfragen“ zurückzusetzen. Testshows shouldShowRequestPermissionRationale() gibt in diesem Fall false zurück, was jedem Code schadet, der sich auf ein “Habe ich schon einmal gefragt”-Flag verlässt.
@itabdullah Der Beispielcode von Google ist nutzlos, da sie nicht einmal den höchstwahrscheinlichen Anwendungsfall “hat der Benutzer die Erlaubnis beim letzten Mal dauerhaft verweigert” berücksichtigt haben. :-/ typisch
– Irgendwer irgendwo
24. Juli 2020 um 20:41 Uhr
CanC
Nach M Vorschau 1, wenn der Dialog angezeigt wird zum ersten Males gibt kein Frag nie wieder Kontrollkästchen.
Wenn der Benutzer die Berechtigungsanfrage ablehnt, wird a Frag nie wieder Kontrollkästchen im Berechtigungsdialog das zweite Mal um Erlaubnis wird gebeten.
Die Logik sollte also so sein:
Um Erlaubnis bitten:
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...
}
Überprüfen Sie, ob die Berechtigung verweigert oder erteilt wurde onRequestPermissionsResult.
Wenn die Erlaubnis zuvor verweigert wurde, gibt es dieses Mal eine Frag nie wieder Kontrollkästchen im Berechtigungsdialog.
Anruf shouldShowRequestPermissionRationale um zu sehen, ob der Benutzer überprüft hat Frag nie wieder. shouldShowRequestPermissionRationale Die Methode gibt nur dann false zurück, wenn der Benutzer ausgewählt hat Frag nie wieder oder die Geräterichtlinie verbietet der App diese Berechtigung:
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...
}
}
}
Sie müssen also nicht nachverfolgen, ob ein Benutzer überprüft hat Frag nie wieder oder nicht.
Ein Punkt zur Klarstellung: shouldShowRequestPermissionRationale() gibt auch false zurück, wenn der Benutzer noch nie nach der Erlaubnis gefragt wurde (dh wenn die Anwendung zum ersten Mal ausgeführt wird). Sie würden nicht auf diesen Fall stoßen, wenn Sie der Logik des bereitgestellten Beispiels folgen. Aber die Formulierung unter 2 ist ein wenig irreführend.
– Ben
23. März 16 um 16:15 Uhr
Ich bin mir nicht sicher, das scheint fehlerhaft zu sein. Woher sollen wir wissen, ob es das erste Mal ist, dass der Benutzer gefragt wird? Ich muss verfolgen, ob der Benutzer gefragt wurde, und wenn ja, muss ich die Logik umkehren. Macht für mich keinen Sinn.
–Daniel F
13. März 17 um 15:38 Uhr
Ich denke, es ist erwähnenswert, wo Sie vorbeikommen context in ActivityCompat.shouldShowRequestPermissionRationale(...) der Parameter ist eigentlich vom Typ Activity. Kann Sie nicht alle betreffen, aber in meinem Fall schon.
– aProperFox
5. September 17 um 23:57 Uhr
Diese Android-Logik ist so verdammt dumm! Es zwingt mich, die anzurufen should im Rückruf UND speichere seinen Zählerwert in NVM, nur um zu wissen, ob ich die Anfrage beim nächsten Öffnen der App erneut anfordern muss! … wow (facepalm) … war es zu schwierig, nur einen Anruf zu tätigen, der eine Statusaufzählung zurückgibt??
– Schockwelle
5. September 18 um 14:40 Uhr
Ich denke, das ist ein großer Fehler von Google. Die offizielle Dokumentation besagt, dass shouldShowRequestPermissionRationale() aufgerufen werden sollte, bevor die Berechtigungen überprüft werden (siehe developer.android.com/training/permissions/requesting#explain), aber alle Antworten in StackOverflow rufen es in onRequestPermissionResult() auf, um zu unterscheiden, ob der Benutzer auf „Nie wieder fragen“ geklickt hat oder nicht.
– Miloš Černilovský
2. April 19 um 7:21 Uhr
muthuraj
Ich hatte das gleiche Problem und ich habe es herausgefunden. Um das Leben viel einfacher zu machen, habe ich eine util-Klasse geschrieben, um Laufzeitberechtigungen zu handhaben.
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();
}
}
Jetzt müssen Sie nur noch die Methode anwenden checkErlaubnis mit richtigen Argumenten.
Hier ist ein Beispiel,
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();
}
});
Fall 1: Die App hat keine Berechtigung und der Benutzer wurde zuvor nicht um die Berechtigung gebeten. In diesem Fall gibt shouldShowRequestPermissionRationale() false zurück, da dies das erste Mal ist, dass wir den Benutzer fragen.
Fall 2: Der Benutzer hat die Berechtigung verweigert und “Nicht erneut fragen” ausgewählt, auch in diesem Fall wird shouldShowRequestPermissionRationale() false zurückgeben.
Ich möchte den Benutzer in Fall 2 auf die Einstellungsseite der App schicken. Wie unterscheide ich diese beiden Fälle?
Sie erhalten einen Rückruf onPermissionAsk für Fall 1 und onPermissionDeaktiviert für Fall 2.
Viel Spaß beim Codieren 🙂
Ausgezeichnete Erklärung Bruder. Habe genau das gleiche Verfahren befolgt. 🙂
– Sumit Jha
24. Dezember 16 um 08:13 Uhr
Was muss ich für diese Aktivität ausfüllen? public void onPermissionAsk() { ActivityCompat.requestPermissions( thisActivity, ... .
– Mardymar
14. Januar 17 um 13:37 Uhr
@Mardymar thisActivity ist nichts als YourActivity.this.
– muthuraj
15. Januar 17 um 17:52 Uhr
wie man mit mehreren Berechtigungen umgeht und wie man diesen Code in das Fragment integriert.
– Taimur
22. Mai 17 um 4:19 Uhr
Vielen Dank – von allen Antworten (einschließlich der akzeptierten / am meisten positiv bewerteten) sind Sie der einzige, der die Frage tatsächlich beantwortet hat
– Adam Burley
25. Juni 21 um 16:41 Uhr
Alex Florescu
AKTUALISIEREN
Ich glaube, dass die Antwort von CanC unten die richtige ist, die befolgt werden sollte. Die einzige Möglichkeit, dies sicher zu wissen, besteht darin, dies im onRequestPermissionResult-Callback mit shouldShowPermissionRationale zu überprüfen.
==
Meine ursprüngliche Antwort:
Die einzige Möglichkeit, die ich gefunden habe, besteht darin, selbst nachzuverfolgen, ob dies das erste Mal ist oder nicht (z. B. mithilfe gemeinsamer Einstellungen). Wenn es nicht das erste Mal ist, dann verwenden shouldShowRequestPermissionRationale() differenzieren.
Siehe auch: Android M – Laufzeitberechtigung prüfen – Wie kann festgestellt werden, ob der Benutzer „Nie wieder fragen“ aktiviert hat?
Ja, sogar ich stimme zu, dass die Methode von CanC diejenige ist, die befolgt werden sollte. Ich werde es als akzeptierte Antwort markieren.
– akshayt23
11. Januar 16 um 10:36 Uhr
So wie ich es verstehe, führt shouldShowRequestPermissionRationale() eine Reihe von Anwendungsfällen unter der Haube aus und benachrichtigt die App, ob eine Erklärung zu den angeforderten Berechtigungen angezeigt werden soll oder nicht.
Die Idee hinter den Run Time-Berechtigungen ist, dass der Benutzer die Berechtigungsanfrage meistens mit Ja beantwortet. Auf diese Weise muss der Benutzer nur einen Klick ausführen. Natürlich sollte die Anfrage im richtigen Kontext verwendet werden – dh nach der Kameraerlaubnis fragen, wenn die Schaltfläche “Kamera” gedrückt wird.
Wenn der Benutzer die Anfrage ablehnt, aber nach einiger Zeit wieder auf die Schaltfläche „Kamera“ drückt, wird shouldShowRequestPermissionRationale() „true“ zurückgeben, sodass die App eine aussagekräftige Erklärung anzeigen kann, warum die Erlaubnis angefordert wird und warum die App dies nicht tut funktionieren auch ohne richtig. Normalerweise würden Sie in diesem Dialogfenster eine Schaltfläche zum erneuten Verweigern/späteren Entscheiden und eine Schaltfläche zum Erteilen der Berechtigungen anzeigen. Die Schaltfläche Berechtigungen erteilen im Begründungsdialogfeld sollte die Berechtigungsanfrage erneut starten. Dieses Mal hat der Benutzer auch ein Kontrollkästchen “Nie wieder anzeigen”. Sollte er sich entscheiden, es auszuwählen und die Erlaubnis erneut zu verweigern, würde es das Android-System darüber informieren, dass sich der Benutzer und die App nicht auf derselben Seite befinden. Diese Aktion hätte zwei Konsequenzen: shouldShowRequestPermissionRationale() gibt immer „false“ zurück, und die Methode „requestPermissions()“ zeigt keinen Dialog an, sondern gibt direkt denied an den Callback „onRequestPermissionsResult“ zurück.
Aber es gibt auch ein anderes mögliches Szenario, in dem onRequestPermissionsResult verwendet werden könnte. Einige Geräte können beispielsweise eine Geräterichtlinie haben, die die Kamera deaktiviert (funktioniert für CIA, DARPA usw.). Auf diesen Geräten gibt onRequestPermissionsResult immer „false“ zurück und die Methode „requestPermissions()“ lehnt die Anfrage stillschweigend ab.
Poste einfach eine andere Option, falls jemand Lust dazu hat. Sie können verwenden EasyPermissions die von Google selbst bereitgestellt wurde, um, wie gesagt, “Android M-Systemberechtigungen zu vereinfachen”.
Dann musst du nicht hantieren shouldShowRequestPermissionRationale direkt.
warum ich dieses Projekt nicht vorher gesehen habe 🙂
– Vlad
4. Oktober 17 um 13:59 Uhr
Das Problem mit EasyPermissions bleibt fast das gleiche. Fragen permissionPermanentlyDenied intern ruft nur an shouldShowPermissionsRationale und kehrt zurück true für den Fall, dass der Benutzer nie aufgefordert wurde, Berechtigungen zu erteilen.
– hgoebl
21. Oktober 18 um 9:46 Uhr
bmjohns
Wenn jemand an einer Kotlin-Lösung interessiert ist, habe ich die @muthuraj-Antwort so umgestaltet, dass sie in Kotlin ist. Außerdem wurde es ein wenig modernisiert, um einen Vervollständigungsblock anstelle von Zuhörern zu haben.
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)
}
}
}
warum ich dieses Projekt nicht vorher gesehen habe 🙂
– Vlad
4. Oktober 17 um 13:59 Uhr
Das Problem mit EasyPermissions bleibt fast das gleiche. Fragen permissionPermanentlyDenied intern ruft nur an shouldShowPermissionsRationale und kehrt zurück true für den Fall, dass der Benutzer nie aufgefordert wurde, Berechtigungen zu erteilen.
– hgoebl
21. Oktober 18 um 9:46 Uhr
Überprüfen Sie diese Implementierung. funktioniert ziemlich gut für mich. Im Grunde überprüfen Sie die Berechtigungen in der checkPermissions () -Methode, indem Sie eine Liste von Berechtigungen übergeben. Sie überprüfen das Ergebnis der Berechtigungsanfrage auf onRequestPermissionsResult(). Mit der Implementierung können Sie beide Fälle ansprechen, wenn der Benutzer “Nie wieder fragen” auswählt oder nicht. Falls er in dieser Implementierung “Nie wieder fragen” auswählt, hat der Dialog eine Option, ihn zur App-Einstellungsaktivität zu bringen.
All dieser Code befindet sich in meinem Fragment. Ich dachte, es wäre besser, eine spezialisierte Klasse dafür zu erstellen, wie einen PermissionManager, aber ich bin mir nicht sicher.
/**
* 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);
}
.
7587000cookie-checkAndroid M-Berechtigungen: Verwirrt bei der Verwendung der Funktion shouldShowRequestPermissionRationale()yes
Die akzeptierte Antwort ist gut. Alternativ können Sie auch eine gemeinsame Voreinstellung verwenden, um zu wissen, ob die App die Erlaubnis zuvor angefordert hat. Wirf das einfach raus, falls es für die Situation eines anderen besser geeignet ist.
– Rockin4Life33
7. Mai 16 um 18:47 Uhr
Es gibt auch Fall 3: Der Benutzer wurde nach der Berechtigung gefragt und diese erteilt/verweigert, aber er hat die Berechtigungseinstellungen verwendet, um wieder auf „jedes Mal nachfragen“ zurückzusetzen. Testshows
shouldShowRequestPermissionRationale()
gibt in diesem Fall false zurück, was jedem Code schadet, der sich auf ein “Habe ich schon einmal gefragt”-Flag verlässt.– Logan-Abholung
28. Februar 18 um 6:53 Uhr
Hier ist ein Google-Beispiel, das die Best Practices in zeigt
permissions
auf Android. github.com/android/permissions-samples– Itabdullah
22. Januar 2020 um 17:52 Uhr
@itabdullah Der Beispielcode von Google ist nutzlos, da sie nicht einmal den höchstwahrscheinlichen Anwendungsfall “hat der Benutzer die Erlaubnis beim letzten Mal dauerhaft verweigert” berücksichtigt haben. :-/ typisch
– Irgendwer irgendwo
24. Juli 2020 um 20:41 Uhr