Voorbeeld van Android-runtimebevoegdheden

Welkom bij het voorbeeld van Android-runtimepermissies. Met de introductie van Android 6.0 Marshmallow heeft Google de manier veranderd waarop machtigingen door de app worden verwerkt. In deze tutorial zullen we kijken naar de nieuwe Android-runtimepermissies die zijn geïntroduceerd en hoe we ze moeten behandelen. Als ze niet goed worden behandeld, kan dit leiden tot crashes van de applicatie.

Wat zijn Android-runtimepermissies?

Met de introductie van Android 6.0 (SDK 23) worden gebruikers tijdens runtime gevraagd om specifieke machtigingen wanneer deze nodig zijn. Dus de eerste vraag die bij ons opkomt, is – Zullen oudere apps draaien op Android Marshmallow? Het antwoord is ja als de targetSdkVersion 22 of lager is. Zo ondersteunen Android-runtimepermissies achterwaartse compatibiliteit. Dit betekent echter niet dat we met het oude model van machtigingen kunnen werken door de SDK-versie op 22 in te stellen. Een gebruiker die Marshmallow gebruikt, kan de gevaarlijke machtigingen intrekken (we zullen later de gevaarlijke en normale machtigingen bespreken) via Instellingen->Apps->Machtigingen. In het geval dat we proberen een functie aan te roepen die een machtiging vereist die de gebruiker nog niet heeft verleend, zal de functie plotseling een uitzondering (java.lang.SecurityException) veroorzaken die leidt tot het crashen van de applicatie. Daarom moeten we dit nieuwe Android-permissiemodel implementeren in onze toepassing.

Gevaarlijke en Normale Android-machtigingen

Android definieert sommige machtigingen als gevaarlijk en sommige als normaal. Het gemeenschappelijke in beide types is dat ze moeten worden gedefinieerd in het Manifest-bestand. Vanaf Android 6.0 worden alleen gevaarlijke machtigingen tijdens runtime gecontroleerd, normale machtigingen niet. Een voorbeeld van een normale machtiging is android.permission.INTERNET. Gevaarlijke machtigingen zijn gegroepeerd in categorieën die het voor de gebruiker gemakkelijker maken om te begrijpen wat ze toestaan dat de applicatie doet. Als de gebruiker één machtiging in een groep/categorie accepteert, accepteert hij de hele groep. Een voorbeeld van een gevaarlijke machtiging is android.permission.FINE_LOCATION en android.permission.COARSE_LOCATION. Het inschakelen van een van de locatiemachtigingen schakelt allemaal in.

Verzoeken om Android-runtime-machtigingen

De methode `requestPermissions(String[] permissions, int requestCode);` is een openbare methode die wordt gebruikt om gevaarlijke toestemmingen aan te vragen. We kunnen om meerdere gevaarlijke toestemmingen vragen door een string-array van toestemmingen door te geven. Opmerking: Android-toestemmingen die tot twee verschillende groepen behoren, zouden de gebruiker voor elke groep afzonderlijk een dialoogvenster tonen. Als ze tot dezelfde groep behoren, wordt slechts één dialoogvenster weergegeven. De resultaten van de verzoeken worden doorgegeven aan de methode `onRequestPermissionResult`. Voorbeeld: Laten we zeggen dat we toegang willen tot de camera en de locatie in onze app. Beide zijn gevaarlijke toestemmingen. We tonen een prompt waarin toegang tot deze toestemmingen wordt gevraagd wanneer de applicatie wordt gestart. Laten we de toestemmingen toevoegen aan een string-array en de methode `requestPermissions` aanroepen zoals hieronder getoond:

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;

    }

}

Nu willen we niet dat de gebruiker toestemmingen blijft accepteren die hij al heeft geaccepteerd. Zelfs als de toestemming eerder is verleend, is het noodzakelijk om opnieuw te controleren om er zeker van te zijn dat de gebruiker die toestemming later niet heeft ingetrokken. Hiervoor moet de volgende methode voor elke toestemming worden aangeroepen.

checkSelfPermission(String perm);

Het retourneert een integerwaarde van PERMISSION_GRANTED of PERMISSION_DENIED. Opmerking: Als een gebruiker een toestemming weigert die cruciaal is in de app, wordt shouldShowRequestPermissionRationale(String permission); gebruikt om de gebruiker de noodzaak van de toestemming uit te leggen. Laten we een applicatie ontwikkelen die controleert of de toestemming al aanwezig is. Zo niet, dan wordt deze tijdens runtime aangevraagd.

Projectstructuur van Android-runtime-machtigingen

Code voor Android-runtime-machtigingen

De content_main.xml bevat de twee knoppen om machtigingen te controleren en aan te vragen.

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

De MainActivity.java is als volgt gedefinieerd.

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

}

Opmerking: Voeg de machtigingen toe die op runtime moeten worden gecontroleerd in het Manifest-bestand boven de toepassingsmarkering als;

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

In de bovenstaande code worden de twee machtigingen die worden gecontroleerd en aangevraagd, CAMERA en LOCATION, gecontroleerd. Door de volledige klasse naam van de statische machtiging te importeren, kunnen we slechts het PERMISSION object schrijven in plaats van het volledig gekwalificeerde pad. checkPermission() roept checkSelfPermission aan voor elke machtiging. requestPermission() roept ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE); aan. onRequestPermissionsResult controleert of de machtigingen zijn verleend of niet. In onze code wordt, als beide machtigingen niet zijn verleend, een waarschuwingsvenster weergegeven waarin de verplichte noodzaak om de machtigingen aan te vragen wordt getoond. Om dit te doen, wordt shouldShowRequestPermissionRationale(String permission) aangeroepen, wat een waarschuwingsvenster oproept dat de noodzaak van de machtigingen laat zien. U kunt de machtigingen handmatig intrekken via Instellingen->Apps->Machtigingen. Opmerking: De methoden specifiek voor runtime-machtigingen zijn alleen beschikbaar vanaf API 23. Daarom wordt bij elke methode de volgende voorwaarde gecontroleerd:

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

De uitvoer van het voorbeeld van de Android-runtime-machtigingen in actie wordt hieronder gegeven. Hiermee komt een einde aan deze tutorial. U kunt het definitieve Android Runtime Permissions-project downloaden via de onderstaande link.

Download Android Runtime Permission Voorbeeldproject

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

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