1

Question...

I'm trying to implement runtime permissions in Android Marshmallow while maintaining backwards compatibility. I've done this successfully before, I'm not sure what's different this time. I've tested it on a physical Note 5 (running Mallow) and using the emulator set up on Marshmallow, neither works.

I realize "it doesn't work" isn't super helpful, but I don't know what else to say, nothing happens. The app doesn't crash it just hangs after requestPermissions(perms, 222) is called.

What am I doing wrong?

Details...

Relevant parts of my activity:

public class HomeActivity extends Activity implements View.OnClickListener {

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.btn_home_scancertificate:
                if (ContextCompat.checkSelfPermission(HomeActivity.this, "android.permission.CAMERA") != PackageManager.PERMISSION_GRANTED){
                    showNoPermDialog();
                }else {
                    AppData.ActionType = ActionType.SCAN_CERTIFICATE;
                    intent = new Intent(HomeActivity.this, CaptureActivity.class);
                    startActivity(intent);
                }
                break;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        Log.e("HomeActivity", "Permissions results...");

        switch (requestCode) {
            case 222: {

                boolean granted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
                Log.e("HomeActivity", granted?"granted permission":"denied permission");

                AppData.ActionType = ActionType.SCAN_POSTCARD;
                intent = new Intent(HomeActivity.this, CaptureActivity.class);
                startActivity(intent);
            }
        }
    }

    public void getCamPerm(){
        Log.e("HomeActivity", "Build version: "+Build.VERSION.SDK_INT);
        if (Build.VERSION.SDK_INT >= 23) {
            Log.e("HomeActivity", "Getting permissions");

            String[] perms = new String[]{Manifest.permission.CAMERA};
            requestPermissions(perms, 222);
        }
    }

    public void showNoPermDialog(){
        if (Build.VERSION.SDK_INT >= 23) {
            boolean showRationale = shouldShowRequestPermissionRationale(Manifest.permission.CAMERA);

            String status = showRationale ? "showing rationale" : "skipping rationale";
            Log.e("HomeActivity", status);

            if (showRationale) {
                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
                alertDialogBuilder.setTitle("Need permission");
                alertDialogBuilder
                    .setMessage("App requires camera permission for the use of the scanner.")
                    .setCancelable(false)
                    .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                            getCamPerm();
                        }
                    });
                AlertDialog alertDialog = alertDialogBuilder.create();
                alertDialog.show();
            }else getCamPerm();

        }else {

            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
            alertDialogBuilder.setTitle("Need permission");
            alertDialogBuilder
                    .setMessage("App requires permission to use the camera. You have disabled camera permission. Please re-enable this permission thru Settings -> Apps -> Our Town -> Permissions.")
                    .setCancelable(false)
                    .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });
            AlertDialog alertDialog = alertDialogBuilder.create();
            alertDialog.show();
        }
    }

}

When run, only these are logged (onRequestPermissionsResult() is never called at all)

E/HomeActivity: skipping rationale

E/HomeActivity: Build version: 23

E/HomeActivity: Getting permissions

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

EDIT

Only on the first time I click the button, this shows up in the Logcat and seems to be triggered by the call to shouldShowRequestPermissionRationale()

08-17 11:26:36.996 20660-20660/com.ourtownamerica.ourtowntrutrak W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=1035.6592, y[0]=770.2344, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=8508970, downTime=8506643, deviceId=0, source=0x1002 }
08-17 11:26:36.996 20660-20660/com.ourtownamerica.ourtowntrutrak W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=1035.6592, y[0]=770.2344, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=8508970, downTime=8506643, deviceId=0, source=0x1002 }
08-17 11:26:36.996 20660-20660/com.ourtownamerica.ourtowntrutrak W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=1035.6592, y[0]=770.2344, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=8508970, downTime=8506643, deviceId=0, source=0x1002 }
08-17 11:26:36.997 20660-20660/com.ourtownamerica.ourtowntrutrak W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=1035.6592, y[0]=770.2344, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=8508970, downTime=8506643, deviceId=0, source=0x1002 }

EDIT 2

I copied my same original code from the Activity it was in and moved it to the splash activity and it works fine there. It can stay there for now, but If anyone knows what's going on input is still appreciated.

Community
  • 1
  • 1

3 Answers3

0

Well, try using Manifest.permission.CAMERA instead of "android.permission.CAMERA"

also for code quality check whether the android is 23 or not in the button click itself R.id.btn_home_scancertificate:

if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(activity,
            Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {

        showNoPermDialog();

    } else {
        AppData.ActionType = ActionType.SCAN_CERTIFICATE;
                intent = new Intent(HomeActivity.this, CaptureActivity.class);
                startActivity(intent);
    }

    //in alertdialog button simply place this for requesting permission
    ActivityCompat.requestPermissions(activity,
            new String[]{Manifest.permission.CAMERA},
            222);
Kashif Anwaar
  • 728
  • 7
  • 14
  • Thank you. I made both of the changes you suggested but no change. Thing is, `shouldShowRequestPermissionRationale()` always returns false because the dialog hasn't opened yet, so I've not had a chance to deny the permission yet. –  Aug 17 '16 at 14:54
  • I've replaced every occurrence of `getCamPerm()` with a call to `ActivityCompat.requestPermissions()` and that still didn't help. –  Aug 17 '16 at 14:59
0

For M permissions, I've been using the following class from this answer:

public class MarshmallowPermission {
    public static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 2;
    public static final int CAMERA_PERMISSION_REQUEST_CODE = 3;

    public MarshmallowPermission() {
    }

    public boolean checkPermissionForExternalStorage(Activity activity) {
        if(Build.VERSION.SDK_INT >= 23) {
            int result = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            if(result == PackageManager.PERMISSION_GRANTED) {
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }

    public boolean checkPermissionForCamera(Activity activity) {
        if(Build.VERSION.SDK_INT >= 23) {
            int result = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
            if(result == PackageManager.PERMISSION_GRANTED) {
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }

    public void requestPermissionForExternalStorage(Activity activity) {
        if(ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            Toast.makeText(activity,
                    "External Storage permission needed. Please allow in App Settings for additional functionality.",
                    Toast.LENGTH_LONG).show();
        } else {
            ActivityCompat.requestPermissions(activity,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
        }
    }

    public void requestPermissionForCamera(Activity activity) {
        if(ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)) {
            Toast.makeText(activity,
                    "Camera permission needed. Please allow in App Settings for additional functionality.",
                    Toast.LENGTH_LONG).show();
        } else {
            ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
        }
    }
}

Works like this:

        if(marshmallowPermission.checkPermissionForCamera(activity)) {
            // has camera
        } else {
            marshmallowPermission.requestPermissionForCamera(activity);
        }

And handle in the Activity callback

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    // handle request code
}
Community
  • 1
  • 1
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • Thank you for the class. No change. At this point I'm convinced the issue is related to the Warnings in the Logcat rather than the actual permissions related code. I will have to some research on them.. –  Aug 17 '16 at 16:05
-1

The trouble likely stems from calling shouldShowRequestPermissionRationale() before ever asking for the permission. There's a bug logged against this (here) and it's especially bad since some of Google's examples/tutorials follow this pattern.

To resolve it, check for the permissions and if you have not been grated them, request them. In the onRequestPermissionsResult() check to see if you should show rationale. This will only return true if the user has rejected the permission and they have not selected the option to not be asked again.

You may find it easier to work with this and the recommended UX guidelines by using the Andele library.

Larry Schiefer
  • 15,687
  • 2
  • 27
  • 33