Android 6.0 Runtime Permissions

(a code lab)

Code Lab Objective

Experiment with Android 6.0 Runtime Permissions

  • You do the experimenting!
  • Or, let the presenter do the experimenting, while you sit back and relax, as why should you do all the work?

Runtime Permissions

Legacy Apps

(targetSdkVersion < 23)

  • No code changes
  • Behavior akin to "app ops"
    • User can revoke dangerous permissions at runtime
    • Affected APIs return bogus results

Runtime Permissions

Modern Apps

(targetSdkVersion >= 23)

  • Same uses-permission elements
  • Must request dangerous permissions
    • Modal dialog-style UI
    • User can accept, deny, or deny with extreme prejudice

Runtime Permission Mechanics


  • Context and ContextCompat
  • Given name of permission, tells you if you have it

Runtime Permission Mechanics


  • Activity and ActivityCompat
  • Given array of permission names, prompts user to accept/deny them
    • One "pane" to dialog per permission group
    • Get result in onRequestPermissionsResult()

Runtime Permission Mechanics


  • Activity and ActivityCompat
  • Given permission name, returns true if...
    • have never asked for this permission, or...
    • asked, the user denied it, but the user has not blocked further requests
  • Use: educate user about upcoming permission request

Possible Runtime Permission States

  • We have never asked for the permission
  • We asked for the permission, and it was granted
  • We asked for the permission, and it was denied
  • We asked for the permission, and it was denied, and the user took out a restraining order against us

Code Lab Time!

Code Lab Resources

  • Starter project
  • PDF with instructions
  • Finished project ...if you just want to see the results

Task #0: Install the Android 6.0 SDK

If you have done this already, great!
If you have not done this already... just sit back and watch!

Task #1: Import the Starter Project

  • file
  • Unzip in some likely spot
  • File > New... > Import Project from Android Studio

Reviewing the Sample App

  • Landscape and portrait layouts, two big buttons
    • Take a picture
    • Record a video
  • Dependencies
    • Icon button library
    • CWAC-Cam2 for camera stuff

Task #2: Upgrade Gradle for Android 6.0

  • compileSdkVersion 23
  • buildToolsVersion "23.0.0"
  • targetSdkVersion 23

So, What Are We Gonna Do About Permissions?

  • Ask for CAMERA and WRITE_EXTERNAL_STORAGE on first run, as the app is totally useless without them
  • Ask for RECORD_AUDIO when they click the "Record Video" button, as we will not need it before then
  • Ask for whatever permissions we do not hold when they click a button that needs them
  • If they deny permissions, then click a button, explain why we are going to ask for the permissions again

Task #3: Add Fields for First-Run Detection

private static final String PREF_IS_FIRST_RUN="firstRun";
private SharedPreferences prefs;

Task #4: Initialize the Preferences

Add the following to onCreate():

Task #5: Use the Preferences to Track the First Run

private boolean isFirstRun() {
  boolean result=prefs.getBoolean(PREF_IS_FIRST_RUN, true);
  if (result) {
    prefs.edit().putBoolean(PREF_IS_FIRST_RUN, false).apply();

Task #6: Check for First Run

Add the following to the bottom of onCreate():
if (isFirstRun()) {
  // TODO

Task #7: Add Some Static Imports

import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;

Task #8: List our Take-Picture Permissions

private static final String[] PERMS_TAKE_PICTURE={

Task #9: Add Our Take-Picture Permission Result Code

private static final int RESULT_PERMS_INITIAL=1339;

Task #10: Add the Support Library for Permission Compatibility Code

dependencies {
  compile 'com.commonsware.cwac:cam2:0.2.+'
  compile 'com.githang:com-phillipcalvin-iconbutton:1.0.1@aar'
  compile ''

Task #11: Ask for Permission

if (isFirstRun()) {
  ActivityCompat.requestPermissions(this, PERMS_TAKE_PICTURE,

Task #12: Add Permission Callback Stub

public void onRequestPermissionsResult(int requestCode,
  String[] permissions, int[] grantResults) {
    // TODO

Task #13: Try It Out!

  • Run the app ...and it should prompt you for permissions
  • Press BACK
  • Run the app again ...and it should not prompt you for permissions
  • Uninstall the app we start from scratch with permissions on the next run

Task #14: Create a Permission-Check Helper Method

private boolean hasPermission(String perm) {
  return(ContextCompat.checkSelfPermission(this, perm)==

Task #15: See If We Can Take a Picture

private boolean canTakePicture() {
  return(hasPermission(CAMERA) &&

Task #16: No, I Mean See If We Can Take a Picture

public void takePicture(View v) {
  if (canTakePicture()) {

Task #17: See If We Should Show Some Rationale

private boolean shouldShowTakePictureRationale() {
            CAMERA) ||

Task #18: Use That New Method, As It Is Lonely

public void takePicture(View v) {
  if (canTakePicture()) {
  else if (shouldShowTakePictureRationale()) {
    // TODO

Task #19: Add a TextView As Our "Breadcrust"

  • @+id/breadcrust
  • visibility set to gone
  • Add to both layout and layout-land

Task #20: Find Our Breadcrust

  • private TextView breadcrust; as field
  • breadcrust=(TextView)findViewById(; in onCreate()

Task #21: Define a Picture Rationale Message

You need to grant us
permission! Tap the Take Picture button again, and we will ask
for permission.

Task #22: Define Another Result Code

private static final int RESULT_PERMS_TAKE_PICTURE=1340;

Task #23: Net the Permissions

  • requestPermissions() prompts user for everything we ask for ...even if they granted the permission to us before
  • This is an icky method, too big for this slide

Task #24: Show Rationale When Needed

"What is it that you want?"
"I want the code!"
"You can't handle the code!"
(...or at least this slide can't)

Task #25: Deal with the Results

  • If we requested permissions, and we can take a picture, go ahead
  • If we requested permissions, cannot take a picture, but should show rationale, do that
  • Otherwise, we're stuck
  • (and, yes, the code is too long for the slide here too)

Task #26: Try It Out!

  • Run the app, reject one of the permissions
  • Tap the picture button, get rationale
  • Tap the picture button again, reject the permission again
  • Uninstall the app

Task #27: Once More, From the Top, with Video

private boolean canRecordVideo() {
  return(canTakePicture() && hasPermission(RECORD_AUDIO));

Task #28: Only Record If We Can

public void recordVideo(View v) {
  if (canRecordVideo()) {

Task #29: U Can Needz Video Rationale?

private boolean shouldShowRecordVideoRationale() {
  return(shouldShowTakePictureRationale() ||

Task #30: Ask All the Permissions! And, Um, Results Too!

private static final String[] PERMS_ALL={
private static final int RESULT_PERMS_RECORD_VIDEO=1341;

Task #31: Really Record the Video. Really.

(pretend that there is some code here)

Task #32: Handle the Results

(did I mention that runtime permissions are tedious?)

Task #33: Configuration Changes. Ugh.

private static final String STATE_BREADCRUST=

protected void onSaveInstanceState(Bundle outState) {

  if (breadcrust.getVisibility()==View.VISIBLE) {

Task #33½: Configuration Changes. Ugh.

protected void onRestoreInstanceState(Bundle savedInstanceState) {

  CharSequence cs=savedInstanceState.getCharSequence(STATE_BREADCRUST);
  if (cs!=null) {