Skip to content

Instantly share code, notes, and snippets.

@lopspower
Last active December 25, 2019 08:33
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save lopspower/01aa324dae5875f27cf9 to your computer and use it in GitHub Desktop.
Save lopspower/01aa324dae5875f27cf9 to your computer and use it in GitHub Desktop.
Android M Permissions

Android M Permissions

Twitter

1) Android Permissions

Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. This approach streamlines the app install process, since the user does not need to grant permissions when they install or update the app. It also gives the user more control over the app's functionality; for example, a user could choose to give a camera app access to the camera but not to the device location. The user can revoke the permissions at any time, by going to the app's Settings screen.

Normal and Dangerous Permissions

System permissions are divided into several protection levels. The two most important protection levels to know about are normal and dangerous permissions:

  • Normal permissions cover areas where your app needs to access data or resources outside the app's sandbox, but where there's very little risk to the user's privacy or the operation of other apps. For example, permission to turn on the flashlight is a normal permission. If an app declares that it needs a normal permission, the system automatically grants the permission to the app. For a full listing of the current normal permissions, see Normal permissions.
  • Dangerous permissions cover areas where the app wants data or resources that involve the user's private information, or could potentially affect the user's stored data or the operation of other apps. For example, the ability to read the user's contacts is a dangerous permission. If an app declares that it needs a dangerous permission, the user has to explicitly grant the permission to the app.

Permission groups

All dangerous Android system permissions belong to permission groups. If the device is running Android 6.0 (API level 23) and the app's targetSdkVersion is 23 or higher, the following system behavior applies when you app requests a dangerous permission:

  • If an app requests a dangerous permission listed in its manifest, and the app does not currently have any permissions in the permission group, the system shows a dialog box to the user describing the permission group that the app wants access to. The dialog box does not describe the specific permission within that group. For example, if an app requests the READ_CONTACTS permission, the system dialog box just says the app needs access to the device's contacts. If the user grants approval, the system gives the app just the permission it requested.
  • If an app requests a dangerous permission listed in its manifest, and the app already has another dangerous permission in the same permission group, the system immediately grants the permission without any interaction with the user. For example, if an app had previously requested and been granted the READ_CONTACTS permission, and it then requests WRITE_CONTACTS, the system immediately grants that permission. Any permission can belong to a permission group, including normal permissions and permissions defined by your app. However, a permission's group only affects the user experience if the perimission is dangerous. You can ignore the permission group for normal permissions.

If the device is running Android 5.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions at install time. Once again, the system just tells the user what permission groups the app needs, not the individual permissions

2) Check For Permissions

If your app needs a dangerous permission, you must check whether you have that permission every time you perform an operation that requires that permission. The user is always free to revoke the permission, so even if the app used the camera yesterday, it can't assume it still has that permission today.

To check if you have a permission, call the ContextCompat.checkSelfPermission() method. For example, this snippet shows how to check if the activity has permission to write to the calendar:

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

If the app has the permission, the method returns PackageManager.PERMISSION_GRANTED, and the app can proceed with the operation. If the app does not have the permission, the method returns PERMISSION_DENIED, and the app has to explicitly ask the user for permission.

3) Request Permissions

If your app needs a dangerous permission that was listed in the app manifest, it must ask the user to grant the permission. Android provides several methods you can use to request a permission. Calling these methods brings up a standard Android dialog, which you cannot customize.

Explain why the app needs permissions

In some circumstances, you might want to help the user understand why your app needs a permission. For example, if a user launches a photography app, the user probably won't be surprised that the app asks for permission to use the camera, but the user might not understand why the app wants access to the user's location or contacts. Before you request a permission, you should consider providing an explanation to the user. Keep in mind that you don't want to overwhelm the user with explanations; if you provide too many explanations, the user might find the app frustrating and remove it.

One approach you might use is to provide an explanation only if the user has already turned down that permission request. If a user keeps trying to use functionality that requires a permission, but keeps turning down the permission request, that probably shows that the user doesn't understand why the app needs the permission to provide that functionality. In a situation like that, it's probably a good idea to show an explanation.

To help find situations where the user might need an explanation, Android provides a utiltity method, shouldShowRequestPermissionRationale(). This method returns true if the app has requested this permission previously and the user denied the request.

Note: If the user turned down the permission request in the past and chose the Don't ask again option in the permission request system dialog, this method returns false. The method also returns false if a device policy prohibits the app from having that permission.

Request the permissions you need

If your app doesn't already have the permission it needs, the app must call one of the requestPermissions() methods to request the appropriate permissions. Your app passes the permissions it wants, and also an integer request code that you specify to identify this permission request. This method functions asynchronously: it returns right away, and after the user responds to the dialog box, the system calls the app's callback method with the results, passing the same request code that the app passed to requestPermissions().

The following code checks if the app has permission to read the user's contacts, and requests the permission if necessary:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

Note: When your app calls requestPermissions(), the system shows a standard dialog box to the user. Your app cannot configure or alter that dialog box. If you need to provide any information or explanation to the user, you should do that before you call requestPermissions(), as described in Explain why the app needs permissions.

4) Handle the permissions request response

When your app requests permissions, the system presents a dialog box to the user. When the user responds, the system invokes your app's onRequestPermissionsResult() method, passing it the user response. Your app has to override that method to find out whether the permission was granted. The callback is passed the same request code you passed to requestPermissions(). For example, if an app requests READ_CONTACTS access it might have the following callback method:

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

The dialog box shown by the system describes the permission group your app needs access to; it does not list the specific permission. For example, if you request the READ_CONTACTS permission, the system dialog box just says your app needs access to the device's contacts. The user only needs to grant permission once for each permission group. If your app requests any other permissions in that group (that are listed in your app manifest), the system automatically grants them. When you request the permission, the system calls your onRequestPermissionsResult() callback method and passes PERMISSION_GRANTED, the same way it would if the user had explicitly granted your request through the system dialog box.

Note: Your app still needs to explicitly request every permission it needs, even if the user has already granted another permission in the same group. In addition, the grouping of permissions into groups may change in future Android releases. Your code should not rely on the assumption that particular permissions are or are not in the same group.

For example, suppose you list both READ_CONTACTS and WRITE_CONTACTS in your app manifest. If you request READ_CONTACTS and the user grants the permission, and you then request WRITE_CONTACTS, the system immediately grants you that permission without interacting with the user.

If the user denies a permission request, your app should take appropriate action. For example, your app might show a dialog explaining why it could not perform the user's requested action that needs that permission.

When the system asks the user to grant a permission, the user has the option of telling the system not to ask for that permission again. In that case, any time an app uses requestPermissions() to ask for that permission again, the system immediately denies the request. The system calls your onRequestPermissionsResult() callback method and passes PERMISSION_DENIED, the same way it would if the user had explicitly rejected your request again. This means that when you call requestPermissions(), you cannot assume that any direct interaction with the user has taken place.


📚 Best Android Gists

You can see other best Android Gists or offer your just here https://github.com/lopspower/BestAndroidGists 👍.

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
/**
* Copyright (C) 2016 Mikhael LOPEZ
* Licensed under the Apache License Version 2.0
*/
public class PermissionsUtils {
//region MY_PERMISSIONS_REQUEST
public static final int MY_PERMISSIONS_REQUEST_EXAMPLE = 1;
//endregion
public static boolean checkAndRequest(@NonNull final Activity activity, @NonNull final String permission, final int requestCode, String messagePermission, DialogInterface.OnClickListener onCancelListener) {
boolean result = false;
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(messagePermission).setPositiveButton(activity.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode);
}
}).setNegativeButton(activity.getResources().getString(android.R.string.cancel), onCancelListener).show();
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode);
// MY_PERMISSIONS_REQUEST is an app-defined int constant.
// The callback method gets the result of the request.
}
} else {
result = true;
}
return result;
}
public static void startInstalledAppDetailsActivity(final Activity context) {
if (context == null) {
return;
}
final Intent i = new Intent();
i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + context.getPackageName()));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(i);
}
}
/**
* Copyright (C) 2016 Mikhael LOPEZ
* Licensed under the Apache License Version 2.0
*/
public class SampleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
if (PermissionsUtils.checkAndRequest(this, Manifest.permission.YOUR_PERMISSION,
PermissionsUtils.MY_PERMISSIONS_REQUEST_EXAMPLE, "Explain here why the app needs permissions", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// YOUR CANCEL CODE
}
})) {
// YOUR BASE METHOD
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PermissionsUtils.MY_PERMISSIONS_REQUEST_EXAMPLE:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
// YOUR BASE METHOD
} else {
// permission denied
boolean showRationale = shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION);
if (!showRationale) {
// user denied flagging NEVER ASK AGAIN
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("WHY THIS PERM IS MANDATORY TO USED THIS APP")
.setPositiveButton(getResources().getString(android.R.string.ok), (dialog, which) -> {
PermissionsUtils.startInstalledAppDetailsActivity(getActivity());
getActivity().finish();
}).setCancelable(false).show();
} else {
// user denied WITHOUT never ask again
// YOUR CANCEL CODE
}
}
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment