requestpermissions - request permission android
¿Cómo distinguimos Nunca-preguntado de dejar de preguntar en los permisos de ejecución de Android M? (9)
Así que finalmente mi tiempo ha llegado para responder una pregunta de COMMONSWARE
Flujo comercial: -
1. Cuando el usuario haga clic en "denegar permiso" por primera vez, mostraré el cuadro de diálogo de justificación para explicar la necesidad del permiso. Luego, si el usuario hace clic en el botón "cancelar" en el cuadro de diálogo de justificación, mostraré un mensaje que muestra "Por favor, dé permiso para obtener la ubicación".
2. Después de eso, cuando el usuario haga clic en negar permiso (no volver a preguntar) en el cuadro de diálogo de permisos, mostraré un mensaje "Por favor, otorgue permiso de ubicación desde la configuración de la aplicación". Tenga en cuenta que he agregado las palabras "desde la configuración de la aplicación" porque el usuario ha marcado la casilla "no volver a preguntar".
3. Por lo tanto, de ahora en adelante no se mostrará el diálogo de permisos. Tampoco se mostrará el diálogo de justificación.
Entonces, la clave aquí es que si no se muestran tanto el diálogo de permiso como el diálogo de razón, significa que el usuario ha marcado la casilla de verificación "no volver a preguntar".
El código:-
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)){
AlertDialogHelper.showDialogWithYesNoCallback(mContext, getString(R.string.confirm), getString(R.string.please_give_permission_to_get_location), new onItemClickReturnBoolean() {
@Override
public void onItemClick(Boolean status) {
if(status){
ActivityCompat.requestPermissions(SplashScreenActivity.this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
}
else{
ShowToast.showShortToast(SplashScreenActivity.this,getString(R.string.please_give_permission_to_get_location));
finish();
}
}
});
}
else{
ActivityCompat.requestPermissions(this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
}
}
else{
gettingLocationAfterPermissionGranted();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
gettingLocationAfterPermissionGranted();
}
else{
if(ActivityCompat.shouldShowRequestPermissionRationale(SplashScreenActivity.this,Manifest.permission.ACCESS_FINE_LOCATION)){
ShowToast.showShortToast(this,getString(R.string.please_give_permission_to_get_location));
}
else{
ShowToast.showShortToast(this,getString(R.string.please_give_location_permission_from_app_settings));
}
finish();
}
}
}
Consulte este repositorio: https://github.com/debChowdhury/PermissionHelperEasy
Easy peasy
Cuando se trata de los permisos de tiempo de ejecución de M Developer Preview, según Google :
-
Si nunca antes ha solicitado un cierto permiso, solo pídalo
-
Si usted preguntó antes, y el usuario dijo "no", y el usuario intenta hacer algo que necesita el permiso rechazado, debe pedirle al usuario que explique por qué necesita el permiso, antes de continuar solicitando el permiso nuevamente.
-
Si ha preguntado un par de veces antes y el usuario ha dicho "no, y deje de preguntar" (a través de la casilla de verificación en el cuadro de diálogo de permisos de tiempo de ejecución), debe dejar de molestarse (por ejemplo, deshabilitar la interfaz de usuario que requiere el permiso)
Sin embargo, solo tenemos un método,
shouldShowRequestPermissionRationale()
, que devuelve un valor
boolean
, y tenemos tres estados.
Necesitamos una forma de distinguir el estado nunca preguntado del estado de dejar de preguntar, ya que obtenemos
false
de
shouldShowRequestPermissionRationale()
para ambos.
Para los permisos que se solicitan en la primera ejecución de la aplicación, este no es un gran problema.
Hay muchas recetas para determinar que esta es probablemente la primera ejecución de su aplicación (por ejemplo, valor
boolean
en
SharedPreferences
), por lo que asume que si es la primera ejecución de su aplicación, está en el estado nunca solicitado.
Sin embargo, parte de la visión de los permisos de tiempo de ejecución es que no puede solicitarlos por adelantado. Permisos vinculados a funciones marginales que solo puede solicitar más adelante, cuando el usuario toca algo que requiere ese permiso. Aquí, la aplicación puede haberse ejecutado muchas veces, durante meses, antes de que de repente necesitemos solicitar otro permiso.
En esos casos, ¿se supone que debemos rastrear si solicitamos o no el permiso nosotros mismos? ¿O hay algo en la API de Android M que me falta que nos diga si preguntamos antes o no?
Después de probar toda la respuesta aquí y alguna otra publicación en internet.
Llegué a saber que tengo que usar una referencia
isLocationPermissionDialogShown
(por defecto falso) y todo funciona según lo esperado.
-
Si la primera vez pide permiso.
En este caso
shouldShowRequestPermissionRationale
devuelvefalse
yisLocationPermissionDialogShown
tambiénfalse
-
La segunda vez
shouldShowRequestPermissionRationale
devuelvetrue
y al mostrar el diálogo establecemosisLocationPermissionDialogShown
entrue
. y cuando verifiquemos la condición, ambas serántrue
-
Cada vez hasta Nunca preguntar de nuevo marcada
shouldShowRequestPermissionRationale
returntrue
yisLocationPermissionDialogShown
devuelvetrue
-
Si nunca se pregunta de nuevo, se
shouldShowRequestPermissionRationale
false
yisLocationPermissionDialogShown
devuelvetrue
. Que es lo que necesitamos.
Por favor, consulte el ejemplo de trabajo.
public class MainActivity extends AppCompatActivity {
SharedPreferences sharedPreferences;
String locationPermission;
String prefLocationPermissionKey = "isLocationPermissionDialogShown";
private final int PERMISSION_REQUEST_CODE_LOCATION = 1001;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationPermission = Manifest.permission.ACCESS_FINE_LOCATION;
sharedPreferences = getSharedPreferences("configuration", MODE_PRIVATE);
//check for android version
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//Check for permission
if (checkSelfPermission(locationPermission) != PackageManager.PERMISSION_GRANTED) {
//check if clarification dialog should be shown.
if (shouldShowRequestPermissionRationale(locationPermission)) {
showClarificationDialog(locationPermission, PERMISSION_REQUEST_CODE_LOCATION);
} else {
requestPermissions(new String[] { locationPermission}, PERMISSION_REQUEST_CODE_LOCATION);
}
} else {
Log.d("nets-debug", "permission already grranted");
}
}
}
@Override
@TargetApi(Build.VERSION_CODES.M)
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
//for location permission
if (requestCode == PERMISSION_REQUEST_CODE_LOCATION) {
boolean isLocationPermissionDialogShown = sharedPreferences.getBoolean(prefLocationPermissionKey, false);
if (!shouldShowRequestPermissionRationale(locationPermission) && isLocationPermissionDialogShown) {
// user selected Never Ask Again. do something
Log.d("nets-debug", "never ask again");
} else {
// all other conditions like first time asked, previously denied etc are captured here and can be extended if required.
Log.d("nets-debug", "all other cases");
}
}
}
}
@TargetApi(Build.VERSION_CODES.M)
public void showClarificationDialog(final String permission, final int requestCode) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Permission Required");
builder.setMessage("Please grant Location permission to use all features of this app");
builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(prefLocationPermissionKey, true);
editor.apply();
requestPermissions(new String[] {permission}, requestCode);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "This permission required", Toast.LENGTH_LONG).show();
}
});
builder.create().show();
}
}
Espero que esto ayude.
Este es un método para rastrear cuándo se mostró el diálogo de permiso por primera vez, cuando el usuario verificó que nunca volvería a preguntar y cuando el permiso se denegó directamente después de que el usuario verificó nunca volver a preguntar por esto, debemos mantener una marca si el diálogo de justificación del permiso se ha mostrado antes resultar en onRequestPermissionsResult. Llame al método checkPermission () cuando sea necesario.
public boolean mPermissionRationaleDialogShown = false;
public void checkPermission() {
if (ContextCompat.checkSelfPermission(this, "PermissionName")
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")) {
showPermissionRequiredDialog();
} else {
askPermission();
}
} else {
// Permission Granted
}
}
public void askPermission() {
ActivityCompat.requestPermissions(this,
new String[]{"PermissionName"}, permissionRequestCode);
}
public void showPermissionRequiredDialog() {
mPermissionRationaleDialogShown = true;
// Dialog to show why permission is required
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
&& !mPermissionRationaleDialogShown) {
// Permission dialog was shown for first time
} else if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
&& mPermissionRationaleDialogShown){
// User deny permission without Never ask again checked
} else if (!ActivityCompat.shouldShowRequestPermissionRationale(this, PERMISSION_READ_EXTERNAL)
&& mPermissionRationaleDialogShown) {
// User has checked Never ask again during this permission request
} else {
// No permission dialog shown to user has user has previously checked Never ask again. Here we can show dialog to open setting screen to change permission
}
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
No es necesario crear un estado persistente paralelo para el estado de permiso, simplemente puede usar este método que devuelve el estado de permiso actual en cualquier momento:
@Retention(RetentionPolicy.SOURCE)
@IntDef({GRANTED, DENIED, BLOCKED})
public @interface PermissionStatus {}
public static final int GRANTED = 0;
public static final int DENIED = 1;
public static final int BLOCKED = 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;
}
return DENIED;
}
return GRANTED;
}
Advertencia: devuelve BLOQUEADO el primer inicio de la aplicación, antes de que el usuario aceptara / negara el permiso a través de la solicitud del usuario (en dispositivos SDK 23+)
No, no necesita rastrear si solicitó o no el permiso, y no necesita distinguir Nunca-Preguntado de Dejar de Preguntar.
Los estados 1 y 3 son los mismos para el desarrollador de aplicaciones: necesita el permiso y
ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED
, luego solo solicita el permiso a través de
ActivityCompat.requestPermissions()
, cada vez que el usuario toca la función que requiere el permiso, No importa cuántas veces haya solicitado.
El usuario eventualmente lo "Concederá" o "Denegará" con "nunca preguntar de nuevo" marcado.
El diseño NO lo desalienta de abrir el cuadro de diálogo de solicitud de permiso varias veces.
Sin embargo, el diseño lo alienta a explicar el propósito del permiso en algún momento: su estado 2.
shouldShowRequestPermissionRationale()
NO se usa para determinar si debe solicitar permiso, se usa para determinar si debe mostrar explicaciones, ANTES de solicitar con permiso.
Un par de explicaciones más sobre el estado 3:
-
Sí, debemos dejar de molestar al usuario dejando de mostrar la explicación, no detener la solicitud.
Es por eso que proporcionaron
shouldShowRequestPermissionRationale()
. -
No es molesto mantener la solicitud de permiso.
Después de que el usuario eligió "nunca preguntar de nuevo",
ActivityCompat.requestPermissions()
ya no aparecerá el cuadro de diálogo emergente. -
Es mejor deshabilitar la interfaz de usuario relevante cada vez que descubramos que no tenemos el permiso, durante la sesión de un solo usuario.
En lugar de deshabilitar la IU después de
shouldShowRequestPermissionRationale()
return false.
Puedes mirar
here
: hay un diagrama de flujo que explica el proceso bastante bien.
También explica cuándo debe llamar a
shouldShowRequestPermissionRationale()
y cuándo devuelve verdadero.
Básicamente, de acuerdo con la documentación de Android, siempre debe pedir permiso si no lo tiene (Android devolverá automáticamente NEGADO en la devolución de llamada si el usuario dijo que nunca volvería a preguntar) y debería mostrar un mensaje corto si el usuario ya ha rechazado alguna vez en el pasado pero no ha marcado la opción de nunca preguntar nuevamente.
Sé que estoy publicando muy tarde, pero un ejemplo detallado puede ser útil para alguien.
Lo que he notado es que si marcamos el indicador shouldShowRequestPermissionRationale () en el método de devolución de llamada onRequestPermissionsResult (), muestra solo dos estados.
Estado 1: -Return true: - Cada vez que el usuario hace clic en Denegar permisos (incluida la primera vez).
Estado 2: - Devuelve falso: - si el usuario selecciona s “nunca pregunta de nuevo.
Aquí hay un ejemplo con solicitud de permiso múltiple: -
La aplicación necesita 2 permisos al inicio. SEND_SMS y ACCESS_FINE_LOCATION (ambos se mencionan en manifest.xml).
Tan pronto como se inicia la aplicación, solicita múltiples permisos juntos. Si se otorgan ambos permisos, el flujo normal continúa.
public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(checkAndRequestPermissions()) {
// carry on the normal flow, as the case of permissions granted.
}
}
private boolean checkAndRequestPermissions() {
int permissionSendMessage = ContextCompat.checkSelfPermission(this,
Manifest.permission.SEND_SMS);
int locationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
List<String> listPermissionsNeeded = new ArrayList<>();
if (locationPermission != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (permissionSendMessage != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add(Manifest.permission.SEND_SMS);
}
if (!listPermissionsNeeded.isEmpty()) {
ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),REQUEST_ID_MULTIPLE_PERMISSIONS);
return false;
}
return true;
}
En caso de que no se otorguen uno o más permisos, activityCompat.requestPermissions () solicitará permisos y el control irá al método de devolución de llamada onRequestPermissionsResult ().
Debe verificar el valor del indicador shouldShowRequestPermissionRationale () en el método de devolución de llamada onRequestPermissionsResult ().
Solo hay dos casos: -
Caso 1: -Cuando el usuario haga clic en Denegar permisos (incluida la primera vez), volverá a ser verdadero. Entonces, cuando el usuario lo niega, podemos mostrar más explicaciones y seguir preguntando nuevamente.
Caso 2: -Solo si el usuario selecciona "nunca pregunta de nuevo", devolverá falso. En este caso, podemos continuar con una funcionalidad limitada y guiar al usuario a activar los permisos desde la configuración para obtener más funcionalidades, o podemos finalizar la configuración, si los permisos son triviales para la aplicación.
CASO 1
CASO 2
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
Log.d(TAG, "Permission callback called-------");
switch (requestCode) {
case REQUEST_ID_MULTIPLE_PERMISSIONS: {
Map<String, Integer> perms = new HashMap<>();
// Initialize the map with both permissions
perms.put(Manifest.permission.SEND_SMS, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
// Fill with actual results from user
if (grantResults.length > 0) {
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for both permissions
if (perms.get(Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "sms & location services permission granted");
// process the normal flow
//else any one or both the permissions are not granted
} else {
Log.d(TAG, "Some permissions are not granted ask again ");
//permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission
// // shouldShowRequestPermissionRationale will return true
//show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.SEND_SMS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
showDialogOK("SMS and Location Services Permission 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.
break;
}
}
});
}
//permission is denied (and never ask again is checked)
//shouldShowRequestPermissionRationale will return false
else {
Toast.makeText(this, "Go to settings and enable permissions", Toast.LENGTH_LONG)
.show();
// //proceed with logic by disabling the related features or quit the app.
}
}
}
}
}
}
private void showDialogOK(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", okListener)
.create()
.show();
}
Según el ejemplo actual: https://github.com/googlesamples/android-RuntimePermissions/blob/master/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java#L195
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
if (requestCode == REQUEST_CAMERA) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
doThing();
//STORE FALSE IN SHAREDPREFERENCES
} else {
//STORE TRUE IN SHAREDPREFERENCES
}
}
Almacene un booleano en SharedPreferences con la clave como su código de permiso y valor como se indicó anteriormente, para indicar si esa preferencia se ha denegado antes.
Lamentablemente, es probable que no pueda verificar una preferencia que ha sido aceptada y luego denegada mientras su aplicación se está ejecutando. La especificación final no está disponible, pero existe la posibilidad de que su aplicación se reinicie o obtenga valores simulados hasta el próximo lanzamiento.
Tengo un enfoque para la solución de su problema, parece que funciona bastante bien para mí.
Distingo el Nunca Preguntado de Dejar de Preguntar usando las Preferencias Compartidas, te daré un ejemplo de cómo lo uso.
private void requestAccountPermission() {
SharedPreferences mPreferences = getSharedPreferences("configuration", MODE_PRIVATE);
boolean firstTimeAccount = mPreferences.getBoolean("firstTimeAccount", true);
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.GET_ACCOUNTS)) {
// 2. Asked before, and the user said "no"
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_CODE_ACCOUNTS);
}else {
if(firstTimeAccount) {
// 1. first time, never asked
SharedPreferences.Editor editor = mPreferences.edit();
editor.putBoolean("firstTimeAccount", false);
editor.commit();
// Account permission has not been granted, request it directly.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS},REQUEST_CODE_ACCOUNTS);
}else{
// 3. If you asked a couple of times before, and the user has said "no, and stop asking"
// Your code
}
}
}