Exemplo de Permissões de Tempo de Execução do Android

Bem-vindo ao exemplo de permissões de tempo de execução do Android. Com a introdução do Android 6.0 Marshmallow, o Google alterou a forma como as permissões são tratadas pelo aplicativo. Neste tutorial, vamos examinar as novas permissões de tempo de execução do Android que foram introduzidas e como lidar com elas. Se não forem tratadas adequadamente, podem causar falhas no aplicativo.

O que são Permissões de Tempo de Execução do Android?

Com a introdução do Android 6.0 (SDK 23), os usuários são solicitados a conceder algumas permissões específicas durante a execução, quando se tornam necessárias para uso. Portanto, a primeira pergunta que vem à mente é: os aplicativos mais antigos serão executados no Android Marshmallow? A resposta é sim se o targetSdkVersion for 22 ou menos. Assim, as permissões de tempo de execução do Android oferecem suporte à compatibilidade reversa. No entanto, isso não significa que podemos trabalhar com o modelo antigo de permissões definindo a versão do SDK como 22. Um usuário que usa o Marshmallow pode revogar as permissões perigosas (discutiremos as permissões perigosas e normais mais tarde) em Configurações->Aplicativos->Permissões. No caso de tentarmos chamar alguma função que requer uma permissão que o usuário ainda não concedeu, a função lançará repentinamente uma exceção (java.lang.SecurityException) que levará à falha do aplicativo. Portanto, precisamos implementar esse novo modelo de permissões do Android em nossa aplicação.

Permissões perigosas e normais do Android

O Android define algumas permissões como perigosas e outras como normais. O comum entre os dois tipos é que elas precisam ser definidas no arquivo Manifesto. A partir do Android 6.0, apenas as permissões perigosas são verificadas em tempo de execução, as permissões normais não são. Um exemplo de permissão normal é android.permission.INTERNET. As permissões perigosas são agrupadas em categorias que facilitam para o usuário entender o que estão permitindo que o aplicativo faça. Se o usuário aceitar uma permissão em um grupo/categoria, ele aceita o grupo inteiro. Um exemplo de permissão perigosa é android.permission.FINE_LOCATION e android.permission.COARSE_LOCATION. Permitir qualquer uma das permissões de localização habilita todas.

Solicitando Permissões em Tempo de Execução no Android

O método requestPermissions(String[] permissions, int requestCode); é um método público usado para solicitar permissões perigosas. Podemos solicitar várias permissões perigosas passando um array de strings de permissões. Nota: As permissões do Android pertencentes a dois grupos diferentes solicitarão ao usuário um diálogo individual para cada uma delas. Se pertencerem ao mesmo grupo, então apenas um diálogo de solicitação será exibido. Os resultados das solicitações serão passados para o método onRequestPermissionResult. Exemplo: Digamos que queremos acessar a câmera e a localização em nosso aplicativo. Ambos são permissões perigosas. Vamos exibir uma solicitação de acesso a essas permissões quando o aplicativo for iniciado. Vamos adicionar as permissões a um array de strings e chamar o requestPermissions conforme mostrado abaixo:

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;

    }

}

Agora não queremos que o usuário continue aceitando permissões que ele já aceitou. Mesmo que a permissão tenha sido concedida anteriormente, é necessário verificar novamente para ter certeza de que o usuário não revogou posteriormente essa permissão. Para isso, o seguinte método precisa ser chamado para cada permissão.

checkSelfPermission(String perm);

Ele retorna um valor inteiro de PERMISSION_GRANTED ou PERMISSION_DENIED. Nota: Se um usuário recusar uma permissão que seja crítica no aplicativo, então shouldShowRequestPermissionRationale(String permission); é usado para descrever ao usuário a necessidade da permissão. Vamos desenvolver um aplicativo que verifica se a permissão já está presente. Se não estiver, então é solicitada em tempo de execução.

Estrutura do Projeto de Permissões de Tempo de Execução do Android

Código de Permissões de Tempo de Execução do Android

O content_main.xml contém os dois botões para verificar e solicitar permissões.

<?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>

O MainActivity.java está definido como abaixo.

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();
    }

}

Observação: Adicione as permissões que devem ser verificadas em tempo de execução no arquivo Manifest acima da tag de aplicativo como;

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

No código acima, as duas permissões que são verificadas e solicitadas são CAMERA e LOCATION. Importar o nome completo da classe de permissão estática nos permite escrever apenas o objeto PERMISSION em vez do caminho totalmente qualificado. checkPermission() chama o checkSelfPermission em cada uma das permissões. requestPermission() chama ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);. onRequestPermissionsResult verifica se as permissões foram concedidas ou não. Em nosso código, se ambas as permissões não forem concedidas, um diálogo de alerta é exibido mostrando a necessidade obrigatória de solicitar as permissões. Para fazer isso, shouldShowRequestPermissionRationale(String permission) é invocado, o que aciona um diálogo de alerta mostrando a necessidade das permissões. Você pode revogar as permissões manualmente em Configurações->Aplicativos->Permissões. Nota: Os métodos específicos de permissão em tempo de execução estão disponíveis apenas desde a API 23. Portanto, a seguinte condição é verificada em cada um dos métodos:

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

O resultado do aplicativo de exemplo de permissões em tempo de execução do Android em ação é mostrado abaixo. Isso encerra este tutorial. Você pode baixar o projeto final de Permissões em Tempo de Execução do Android no link abaixo.

Baixar Projeto de Exemplo de Permissão em Tempo de Execução do Android

Referência: https://developer.android.com/training/permissions/requesting.html

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