60

I'm having a weird issue that is causing a conflict. I had to switch to native Fragments to fix it, but there are bugs with that.

My original problem: I have a navigation drawer setup with v4 Fragments. To ask for permission in one of my Fragments I call ActivityCompat.requestPermissions(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION, 1); The prompt shows up just fine, but when I accept or deny the permission, nothing happens. The callback onRequestPermissionsResult() is never called. Instead it gets called in the Activity that my Fragments are attached to. Useless to me, I need the callback to work in the Fragment.

With this in mind I was told that I need to use FragmentCompat, but that only works with native Fragments (v13+), so I changed navigation drawer to work from native Fragments instead of the v4 support library Fragments. However, because I'm using AppCompatActivity, certain things do not work, like addToBackStack() and going back to a previous fragment.

Long story short, does anyone know how I can use the v4.Fragment and still call for permission in the Fragment and get the callback to be in the Fragment? I feel like this is a bug in Android that hasn't been addressed but I'm not 100%.

Let me know if you need to see my code, it's just the standard methods that you need for runtime permissions, I would like to work with v4 Fragments though which doesn't work from my understanding.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
RED_
  • 2,997
  • 4
  • 40
  • 59

11 Answers11

72

Adding this to the parent activity works for me:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

Source: https://code.google.com/p/android/issues/detail?id=189121#c5

Honza Zidek
  • 9,204
  • 4
  • 72
  • 118
Samuel Gaona
  • 838
  • 1
  • 7
  • 13
  • 1
    Well done! Send it to everyone and check codec there. Many thanks! – Aleksandar Aug 15 '16 at 10:27
  • 1
    Nice job. Thanks for providing an answer useful in the real world. – Captain Kenpachi Nov 28 '16 at 10:19
  • perfect solution. – Deep Shah Apr 05 '17 at 14:07
  • It is possible in some cases for a fragment to be null yet still be in the fragments array, so a check should be added for safety (or, in Kotlin, you can use `fragment?.onRequestPermissionsResult(...)`) – Ander Jun 02 '17 at 01:58
  • I think you should not use for loop. Because maybe you have an other fragment that listens same permission and it is not at the top of stack and maybe you show a popup or other action in its callback so while it is not visible on screen it will continue its progress. So user may confuse about it. I changed this code to this. `List fragments = getSupportFragmentManager().getFragments(); fragments.get(fragments.size() - 1).onRequestPermissionsResult(requestCode, permissions, grantResults);` Now only the last and visible fragment receive the callback. – KAPLANDROID Oct 04 '17 at 21:01
  • @KAPLANDROID: each of the fragment will have different permission request code, so even if a fragment receives a notification, they will discard it – vodkhang Jan 18 '18 at 04:03
19

If you need to get the permissionResult in fragment v4 make sure you use

Fragment.requestPermission(String[], int);

instead of

AppCompat.requestPermission(Activity, String[], int)

Check out this answer!

Community
  • 1
  • 1
DoruChidean
  • 7,941
  • 1
  • 29
  • 33
18

This behavior seems to be present in the v4 Fragment support class requestPermissions in Fragment. The Activity/FragmentCompat implementations exist for people who wish to use the native classes with the extended functionality on API levels between 11 and 23.

Kaushik
  • 6,150
  • 5
  • 39
  • 54
NasaGeek
  • 2,138
  • 1
  • 15
  • 19
  • 2
    The reason to use Activity/FragmentCompat under my understanding was that it would mean I would not have to check if the user was on Android 6.0, the permissions would just default to permission granted if they were on an old version of Android. With this code I would have to add code for those on API 23 and those that are not, or at least frame my statements differently since Android will not handle it. I suppose it's not available to me then? – RED_ Oct 01 '15 at 15:29
  • 1
    Hmm weird. I tested it and it works perfectly. Even on an older version of Android it just worked seemlessly without asking for the permission. Maybe I got things mixed up, thanks for the tip! +1 and will mark as answer. – RED_ Oct 01 '15 at 15:37
  • This works fine for me aswell. Thanks for this guidance...I wonder how requestPermission method is already available in v4 Fragment – John Nov 20 '15 at 08:34
12

You can use this part of code

requestPermissions(new String[]{Manifest.permission.GET_ACCOUNTS}, PERMISSION_REQUEST_CODE);
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Abubakar
  • 336
  • 4
  • 13
7

I faced the same situation recently, when I needed to check for a permission inside the support fragment and get a callback there.

I was able to use ContextCompat to checkSelfPermission and then as @NasaGeek said called android.support.v4.app.Fragment's requestPermissions to request the permission and then got a call back to onRequestPermissionsResult in v4 Fragment.

Hope this helps.

Thanks.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
3

v4.Fragment works well. I have an issue with nested fragment of v4.Fragment. Permission is asked, but the callback onRequestPermissionsResult() is never called in nested fragment!

Issue opened

android_dev
  • 3,886
  • 1
  • 33
  • 52
3

At the moment the most stable solution is doing it by hand. I myself resolved it simply by notifying child fragments from the parent fragments.

if (fragment.getParentFragment() != null) {
                            Fragment parentFragment = fragment.getParentFragment();
                            try {
                                ChildFragmentPermissionRegistry registry = (ChildFragmentPermissionRegistry) parentFragment;
                                registry.registerPermissionListener((ChildFragmentPermissionCallback) fragment);
                                parentFragment.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_REQUEST_CODE);
                            } catch (ClassCastException e) {
                                Log.e(PermissionVerifier.class.getSimpleName(), e.getMessage());
                            }
                        } else {
                            fragment.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_REQUEST_CODE);
                        }

Where parent fragment implements interface ChildFragmentPermissionRegistry and registers child fragment,

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (callback != null) {
        callback.onRequestPermissionsResult(requestCode, permissions, grantResults);
        callback = null;
    }
}

and child fragments implements ChildFragmentPermissionCallback

and interfaces are something like this:

public interface ChildFragmentPermissionCallback {
    void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
}

public interface ChildFragmentPermissionRegistry {
    void registerPermissionListener(ChildFragmentPermissionCallback callback);
}
1

I don't know if it's recently fixed by google, but I can reach the expected result without doing any tricks.

The most important thing is to call super.onRequestPermissionsResult(requestCode, permissions, grantResults); in the activity, so it will pass the result to fragment if it's requested from fragment.

So, what I do is:

1) in fragment, ask permission using v4 fragment.requestPermissions(permissions, requestCode)

2) in activity's onRequestPermissionsResult, must call super.onRequestPermissionsResult(requestCode, permissions, grantResults);

3) in fragment's onRequestPermissionsResult, write the code I want to handle the result.

YunhaoLIU
  • 805
  • 7
  • 17
0

In my case I have requested the permission from the fragment and also need to get the response in fragment.

But my phone running on android 8.1

so I was need to add one more condition for check this

so eventually there is my solution

private void doOnWazeButtonClick()
{
    int permissionStatus = PackageManager.PERMISSION_DENIED;

    if (getContext() != null)
    {
        permissionStatus = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION);
    }

    if (permissionStatus == PackageManager.PERMISSION_GRANTED)
    {
        showContentWaze();
    }
    else
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        {
            Objects.requireNonNull(getActivity()).requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_PERMISSION_ACCESS_FINE_LOCATION);
        }
        else
        {
            requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_PERMISSION_ACCESS_FINE_LOCATION);
        }
    }
}
Sirop4ik
  • 4,543
  • 2
  • 54
  • 121
0

Check runtime permission from Fragment (the 2021 way)

I have answered this in a different question, here is the link: answer link.

In short, we can now use registerForActivityResult in a fragment. In your fragment's constructor (before onCreate), you initialize ActivityResultLauncher<String[]> activityResultLauncher resultLauncher, and when you need permission, simply invoke resultLauncher.launch(stringArrayOfPermissions). Please check the link on the first line for detailed example.

Qazi Fahim Farhan
  • 2,066
  • 1
  • 14
  • 26
-1

Just use requestPermission("your permission/s in string array",your request code) simply no need to use Fragment.requestPermissons(String[],int );

This method in your fragment calls requestPermissions of android.support.v4.app.Fragment class i.e

public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
        if (mHost == null) {
            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
        }
        mHost.onRequestPermissionsFromFragment(this, permissions, requestCode);
    }
Andrew Coder
  • 246
  • 2
  • 6