Duplicate permission request after orientation change
Asked Answered
H

3

15

Because the Android SDK 23 gives users the possibility to deny apps access to certain functionalities I wanted to update one of my apps to request permissions as it is described in here: https://developer.android.com/preview/features/runtime-permissions.html.

In one of the activities I embed a SupportMapFragment. To make it work you need to have the WRITE_EXTERNAL_STORAGE permission, so I request it when I start the activity which results in a creation of a permission request dialog.

Now the problem is that when the dialog is still open and I rotate the device the activity will be restarted and open a new permission request dialog while the old one is still there. The result is two of those dialogs on top of each other and only one of it being useful.

Is there a way to get rid of the dialog that was started first?

Hazaki answered 26/8, 2015 at 13:15 Comment(2)
Since you did not create the dialog, I am not aware of any way for you to get rid of it. Put a boolean in your saved instance state Bundle indicating that the permission request is outstanding, and do not re-request the permission. Or, wait to request the permission until some form of user input (e.g., tapping on the action bar item that launches the SupportMapFragment), in which case you should be safe, as the user cannot tap on that item again while the dialog is visible.Toni
Yup, that does it. Did not think this would be an option since the "old" dialog would only be tied to the "old" activity but I just tried it and the result is received by the "new" activity.Hazaki
H
17

As CommonsWare said in his comment the best solution is to put a boolean into the savedInstanceState-Bundle to know if the dialog is still open.

Example:

// true if dialog already open
private boolean alreadyAskedForStoragePermission = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(savedInstanceState != null) {
        alreadyAskedForStoragePermission = savedInstanceState.getBoolean(STORAGE_PERMISSION_DIALOG_OPEN_KEY, false);
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    outState.putBoolean(KEY, alreadyAskedForStoragePermission);
}

private void checkStoragePermission(){
    if(alreadyAskedForStoragePermission){
        // don't check again because the dialog is still open
        return;
    }

    if(ActivityCompat.checkSelfPermission(this, STORAGE_PERMISSIONS[0]) != PackageManager.PERMISSION_GRANTED){
        // the dialog will be opened so we have to keep that in memory
        alreadyAskedForStoragePermission = true;
        ActivityCompat.requestPermissions(this, STORAGE_PERMISSIONS, STORAGE_PERMISSION_REQUEST_CODE);
    } else {
        onStoragePermissionGranted();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode){
        case STORAGE_PERMISSION_REQUEST_CODE:
            // the request returned a result so the dialog is closed
            alreadyAskedForStoragePermission = false;

            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                onStoragePermissionGranted();
            }

            break;
    }
}
Hazaki answered 26/8, 2015 at 13:45 Comment(2)
I believe there is a system boolean exists in Bundle savedInstanceState android:hasCurrentPermissionsRequest=true if permission dialog is shown, though didn't find any mention in documentation.Clunk
You don't need to keep a new variable. Android calls onRequestPermissionsResult with an empty permissions array for instances like rotation and automatically displays the permissions dialog again afterwards. So only handle these results if permissions array is not empty. Ie. Just encapsulate everything you do in onRequestPermissionsResult with a permissions not empty check.Sickroom
H
14

As @user1991776 mentioned there is actually an undocumented extra that contains whether or not there is a permission dialog open at the moment, in Activity:

private static final String HAS_CURENT_PERMISSIONS_REQUEST_KEY =
        "android:hasCurrentPermissionsRequest";

However there is a better way. When you request a permission dialog the second time (due to a rotation), Activity automatically cancels the old dialog by calling your onRequestPermissionResult() with empty arrays:

public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
    if (mHasCurrentPermissionsRequest) {
        Log.w(TAG, "Can reqeust only one set of permissions at a time");
        // Dispatch the callback with empty arrays which means a cancellation.
        onRequestPermissionsResult(requestCode, new String[0], new int[0]);
        return;
    }
    Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
    startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
    mHasCurrentPermissionsRequest = true;
}

Or course this behaviour isn't documented because this is Android, and who wants to document complex behaviour?

Anyway you can just always request permissions in onCreate() and then ignore calls to onRequestPermissionsResult() with zero-length permissions arrays.

Housing answered 24/6, 2016 at 11:27 Comment(1)
+ for documenting android=)Straub
M
0

I guess as this is a system dialog you cannot control it. You could instead prevent that your activity gets reloaded if you turn your device.

Mathias answered 26/8, 2015 at 13:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.