Ejemplo de Permisos en Tiempo de Ejecución de Android

Bienvenido al ejemplo de permisos de ejecución de Android. Con la introducción de Android 6.0 Marshmallow, Google ha cambiado la forma en que la aplicación maneja los permisos. En este tutorial, veremos los nuevos permisos de ejecución de Android que se han introducido y cómo manejarlos. Si no se manejan correctamente, puede causar bloqueos de la aplicación.

¿Qué son los permisos de ejecución de Android?

Con la introducción de Android 6.0 (SDK 23), se solicita a los usuarios algunos permisos específicos en tiempo de ejecución cuando se vuelven necesarios para su uso. Entonces, la primera pregunta que nos viene a la mente es: ¿Las aplicaciones antiguas se ejecutarán en Android Marshmallow? La respuesta es si targetSdkVersion es 22 o inferior. Por lo tanto, los permisos de ejecución de Android admiten la compatibilidad hacia atrás. Ahora, esto no significa que podamos trabajar con el antiguo modelo de permisos estableciendo la versión del SDK en 22. Un usuario que utilice Marshmallow puede revocar los permisos peligrosos (discutiremos los permisos peligrosos y normales más adelante) desde Configuración->Aplicaciones->Permisos. En el caso de que intentemos llamar a alguna función que requiera un permiso que el usuario aún no ha otorgado, la función arrojará repentinamente una excepción (java.lang.SecurityException) que provocará que la aplicación se bloquee. Por lo tanto, necesitamos implementar este nuevo modelo de permisos de Android en nuestra aplicación.

Permisos peligrosos y normales de Android

Android define algunos permisos como peligrosos y otros como normales. Lo común en ambos tipos es que necesitan ser definidos en el archivo Manifest. A partir de Android 6.0, solo se verifican los permisos peligrosos en tiempo de ejecución, los permisos normales no lo son. Un ejemplo de permiso normal es android.permission.INTERNET. Los permisos peligrosos se agrupan en categorías que facilitan al usuario entender lo que está permitiendo que la aplicación haga. Si el usuario acepta un permiso en un grupo/categoría, acepta todo el grupo. Un ejemplo de permiso peligroso es android.permission.FINE_LOCATION y android.permission.COARSE_LOCATION. Habilitar cualquiera de los permisos de ubicación habilita todos.

Solicitando Permisos en Tiempo de Ejecución de Android

El método requestPermissions(String[] permissions, int requestCode); es un método público que se utiliza para solicitar permisos peligrosos. Podemos solicitar múltiples permisos peligrosos pasando un array de cadenas de permisos. Nota: Los permisos de Android pertenecientes a dos grupos diferentes solicitarán al usuario un cuadro de diálogo individual para cada uno de ellos. Si pertenecen al mismo grupo, solo se mostrará un cuadro de diálogo. Los resultados de las solicitudes se pasarán al método onRequestPermissionResult. Ejemplo: Supongamos que queremos acceder a la cámara y la ubicación en nuestra aplicación. Ambos son permisos peligrosos. Mostraremos un cuadro de diálogo solicitando acceso a estos permisos cuando se inicie la aplicación. Agreguemos los permisos a un array de cadenas y llamemos a requestPermissions como se muestra a continuación:

String[] perms = {"android.permission.FINE_LOCATION", "android.permission.CAMERA"};

int permsRequestCode = 200; 
requestPermissions(perms, permsRequestCode);

@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){

    switch(permsRequestCode){

        case 200:

            boolean locationAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
            boolean cameraAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;

            break;

    }

}

Ahora no queremos que el usuario siga aceptando permisos que ya haya aceptado. Incluso si el permiso se ha concedido previamente, es necesario verificar nuevamente para asegurarse de que el usuario no lo haya revocado más tarde. Para esto, se debe llamar al siguiente método en cada permiso.

checkSelfPermission(String perm);

Devuelve un valor entero de PERMISSION_GRANTED o PERMISSION_DENIED. Nota: Si un usuario rechaza un permiso que es crítico en la aplicación, entonces se utiliza shouldShowRequestPermissionRationale(String permission); para describir al usuario la necesidad del permiso. Desarrollemos una aplicación que compruebe si el permiso ya está presente. Si no es así, se solicita en tiempo de ejecución.

Proyecto de Estructura de Permisos en Tiempo de Ejecución de Android

Código de Permisos en Tiempo de Ejecución de Android

El archivo content_main.xml contiene dos botones para verificar y solicitar permisos.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.journaldev.runtimepermissions.MainActivity"
    tools:showIn="@layout/activity_main">
    <Button
        android:id="@+id/check_permission"
        android:layout_width="match_parent"
        android:layout_centerInParent="true"
        android:layout_height="wrap_content"
        android:text="Check Permission"/>
    <Button
        android:id="@+id/request_permission"
        android:layout_below="@+id/check_permission"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Request Permission"/>
</RelativeLayout>

La clase MainActivity.java está definida de la siguiente manera:

package com.journaldev.runtimepermissions;

import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;

import android.view.View;
import android.widget.Button;

import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.CAMERA;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int PERMISSION_REQUEST_CODE = 200;
    private View view;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        Button check_permission = (Button) findViewById(R.id.check_permission);
        Button request_permission = (Button) findViewById(R.id.request_permission);
        check_permission.setOnClickListener(this);
        request_permission.setOnClickListener(this);


    }


    @Override
    public void onClick(View v) {

        view = v;

        int id = v.getId();
        switch (id) {
            case R.id.check_permission:
                if (checkPermission()) {

                    Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();

                } else {

                    Snackbar.make(view, "Please request permission.", Snackbar.LENGTH_LONG).show();
                }
                break;
            case R.id.request_permission:
                if (!checkPermission()) {

                    requestPermission();

                } else {

                    Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();

                }
                break;
        }

    }

    private boolean checkPermission() {
        int result = ContextCompat.checkSelfPermission(getApplicationContext(), ACCESS_FINE_LOCATION);
        int result1 = ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA);

        return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
    }

    private void requestPermission() {

        ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_CODE:
                if (grantResults.length > 0) {

                    boolean locationAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    boolean cameraAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;

                    if (locationAccepted && cameraAccepted)
                        Snackbar.make(view, "Permission Granted, Now you can access location data and camera.", Snackbar.LENGTH_LONG).show();
                    else {

                        Snackbar.make(view, "Permission Denied, You cannot access location data and camera.", Snackbar.LENGTH_LONG).show();

                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
                                showMessageOKCancel("You need to allow access to both the permissions",
                                        new DialogInterface.OnClickListener() {
                                            @Override
                                            public void onClick(DialogInterface dialog, int which) {
                                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                                    requestPermissions(new String[]{ACCESS_FINE_LOCATION, CAMERA},
                                                            PERMISSION_REQUEST_CODE);
                                                }
                                            }
                                        });
                                return;
                            }
                        }

                    }
                }


                break;
        }
    }


    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(MainActivity.this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

}

Nota: Agregue los permisos que se deben verificar en tiempo de ejecución en el archivo Manifesto, encima de la etiqueta de la aplicación como;

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

En el código anterior, se verifican y solicitan dos permisos: CAMERA y LOCATION. Importar el nombre de clase completo del permiso estático nos permite escribir solo el objeto PERMISSION en lugar de la ruta completamente calificada. `checkPermission()` llama a `checkSelfPermission` en cada uno de los permisos. `requestPermission()` llama a `ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);`. `onRequestPermissionsResult` verifica si los permisos se otorgan o no. En nuestro código, si no se otorgan ambos permisos, se muestra un cuadro de diálogo de alerta que indica la necesidad obligatoria de solicitar los permisos. Para hacer eso, se invoca `shouldShowRequestPermissionRationale(String permission)`, que muestra un cuadro de diálogo de alerta que indica la necesidad de los permisos. Puedes revocar los permisos manualmente desde Configuración->Aplicaciones->Permisos. Nota: Los métodos específicos de permisos en tiempo de ejecución solo están disponibles desde la API 23. Por lo tanto, se verifica la siguiente condición en cada uno de los métodos:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)

El resultado de la aplicación de ejemplo de permisos en tiempo de ejecución de Android en acción se muestra a continuación. Esto concluye este tutorial. Puedes descargar el proyecto final de Permisos en Tiempo de Ejecución de Android desde el siguiente enlace.

Descargar Proyecto de Ejemplo de Permisos en Tiempo de Ejecución de Android

Referencia: https://developer.android.com/training/permissions/requesting.html

Source:
https://www.digitalocean.com/community/tutorials/android-runtime-permissions-example