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

Bem-vindo ao exemplo de permissões em 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 analisar as novas permissões em tempo de execução do Android que foram introduzidas e como lidar com elas. Se não forem tratadas corretamente, podem causar falhas no aplicativo.

O que são Permissões em 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 em tempo de execução quando estas se tornam necessárias para uso. Então, a primeira pergunta que vem à nossa mente é – Os aplicativos mais antigos funcionarão no Android Marshmallow? A resposta é sim se o targetSdkVersion for 22 ou menos. Assim, as permissões em tempo de execução do Android suportam a compatibilidade com versões anteriores. Agora, isso não significa que podemos trabalhar com o antigo modelo de permissões definindo a versão do sdk como 22. Um usuário que utiliza o Marshmallow pode revogar as permissões perigosas (vamos discutir as permissões perigosas e normais posteriormente) em Configurações->Apps->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 irá lançar imediatamente uma Exceção (java.lang.SecurityException) que levará ao travamento do aplicativo. Portanto, precisamos implementar este novo modelo de permissões do Android em nosso aplicativo.

Permissões perigosas e normais do Android

O Android define algumas permissões como perigosas e outras como normais. A coisa comum em ambos os 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 aceita 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. Habilitar 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 que é usado para solicitar permissões perigosas. Podemos solicitar várias permissões perigosas passando uma matriz de strings de permissões. Nota: As Permissões do Android pertencentes a dois grupos diferentes solicitariam ao usuário um diálogo individual para cada uma delas. Se elas 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 para acessar essas permissões quando o aplicativo for lançado. Vamos adicionar as permissões em uma matriz 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 em 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.

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

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

O arquivo 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 arquivo MainActivity.java é 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();
    }

}

Nota: 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 LOCALIZAÇÃO. A importação do 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, é exibida uma caixa de diálogo de alerta mostrando a necessidade obrigatória de solicitar as permissões. Para fazer isso, shouldShowRequestPermissionRationale(String permission) é invocado, o que chama uma caixa de 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 exemplo de aplicativo de permissões em tempo de execução do Android em ação é apresentado 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