permiso icon indicating copy to clipboard operation
permiso copied to clipboard

IllegalStateException when showing Fragment

Open ScottCooper92 opened this issue 10 years ago • 8 comments

I'm trying to display a DialogFragment when the permissions are denied, to let the user know that they need to accept them before they can continue further into the app.

I have this in onCreate

Permiso.getInstance().requestPermissions(new Permiso.IOnPermissionResult() {
    @Override
    public void onPermissionResult(Permiso.ResultSet resultSet) {
        if (!resultSet.areAllPermissionsGranted()) {
            final ConfirmDialog confirmDialog = ConfirmDialog.newInstance(R.string.permission_title, R.string.permission_text);
            confirmDialog.show(getSupportFragmentManager(), ConfirmDialog.TAG);
        } else {
            //We're good to go!
            performSetup();
        }
    }

    @Override
    public void onRationaleRequested(Permiso.IOnRationaleProvided callback, String...
            permissions) {
        callback.onRationaleProvided();
    }
}, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE);

And get this every time:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=1, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.shortlister.app/com.shortlister.app.presentation.ui.interview.live.InterviewActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
                                                                         at android.app.ActivityThread.deliverResults(ActivityThread.java:3699)
                                                                         at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
                                                                         at android.app.ActivityThread.-wrap16(ActivityThread.java)
                                                                         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                         at android.os.Looper.loop(Looper.java:148)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
                                                                      Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
                                                                         at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1489)
                                                                         at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1507)
                                                                         at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:634)
                                                                         at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:613)
                                                                         at android.support.v4.app.DialogFragment.show(DialogFragment.java:139)
                                                                         at com.shortlister.app.presentation.ui.interview.base.BaseInterviewActivity$1.onPermissionResult(BaseInterviewActivity.java:85)
                                                                         at com.greysonparrelli.permiso.Permiso.onRequestPermissionResult(Permiso.java:153)
                                                                         at com.shortlister.app.presentation.ui.common.BaseActivity.onRequestPermissionsResult(BaseActivity.java:113)
                                                                         at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6553)
                                                                         at android.app.Activity.dispatchActivityResult(Activity.java:6432)
                                                                         at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
                                                                         at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
                                                                         at android.app.ActivityThread.-wrap16(ActivityThread.java) 
                                                                         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
                                                                         at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                         at android.os.Looper.loop(Looper.java:148) 
                                                                         at android.app.ActivityThread.main(ActivityThread.java:5417) 
                                                                         at java.lang.reflect.Method.invoke(Native Method) 
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

ConfirmDialog is a subclass of android.support.v4.app.DialogFragment, if I use android.app.DialogFragment there is no issue so I'm assuming it's a problem with the support library.

Here's the issue I came across when trying to fix it: https://code.google.com/p/android-developer-preview/issues/detail?id=2823

Let me know if this is something I'm doing as opposed to an issue with your library.

ScottCooper92 avatar Nov 23 '15 17:11 ScottCooper92

Hmm, that's interesting. Android seems to think your activity is in the process of finishing. In the comments on the issue filed against Google there's a project that's supposed to be able to reproduce this issue (link). I'll check it out when I get home so I can nail down what's happening here, since I can't reproduce it in the Permiso demo project.

greysonp avatar Nov 23 '15 18:11 greysonp

Ok, so I tried out that project I mentioned and it does, in fact, crash. As you said, the problem goes away when switching to the non-support DialogFragment.

Of course this is Google's issue to solve, not mine, so I'll close this issue. I commented on the bug letting them know it occurs only with the support library. If you find out anything else that I can do, feel free to re-open this issue.

greysonp avatar Nov 24 '15 05:11 greysonp

This happens because onRequestPermissionResult triggers between onPause and onResumeFragments. Before onResumeFragments it's illegal to call commit on FragmentTransaction.

Here is my implementation which fixes this problem. Maybe it's not the best solution, but works :satisfied:.

I'm not sure if the same trick has to be applied to linkToExistingRequestIfPossible.

zokipirlo avatar Feb 12 '16 10:02 zokipirlo

Hi @zokipirlo thanks for looking into this further, I really appreciate it! However, looking at your solution, it's awesome that it works, but I'm not sure if it's the best thing to include in Permiso. First, it's caused by a bug in the support library which should be fixed soon. It also changes the public API. If it was a small internal change that could circumvent the bug I might be up for it, but I don't want to make any actual interface changes to deal with this (hopefully) temporary issue.

I checked the issue again, and I think they're making some progress. I also saw a good suggestion in the comments made a couple days ago to get around this issue:

  • If you need to show a fragment in onPermissionResult(), instead of setting it, set some boolean variable to true.
  • In onResume() of your activity, if the variable is true, show your Fragment then.

Also, it's worth reminding that this only happens when using the Fragment class from the support library, so if you can switch to the regular Fragment class, then that will also solve the problem.

greysonp avatar Feb 12 '16 17:02 greysonp

Hi, I wish it could be so simple. I don't mind if you don't include it in your library, but if you change your mind I could make a pull request ;)

I have done that what you suggests. But onResume() is not good. Check here. The only difference is that I don't store a boolean, but all request codes for which response come after onPause.

I think it's not possible to switch to regular Fragment when using AppCompatActivity and there are also some libraries which uses support Fragments.

And this bug is open for 4 months now, so I don't think they will fix that in near future (if ever).

I can also make a PermisoSupport if you think it's better that way.

It's a great library, I really like it. Maybe will someone else also need that fix before Google do something on that.

zokipirlo avatar Feb 12 '16 18:02 zokipirlo

You're right, you should use onResumeFragments() if that method is available to you (i.e. you're using FragmentActivity or AppCompatActivity). Also, if you're trying to solve this generically then you'll have to keep a mapping of requestCodes, but if you're an end user just trying to show a fragment, in most cases a simple flag will work.

I understand that they've been slow on the bug, but it was reopened about 2 months ago and has been assigned, so hopefully it'll be fixed soon-ish. It just doesn't feel right to introduce more Permiso boilerplace to fix a bug in the support library that not everyone will even run into.

I've got a camping trip this weekend, so I won't be around, but I understand your points and would be willing to look into alternate solutions, as I do think this could be helpful for people running into this bizarre issue. Maybe it's the PermisoSupport class you suggested, maybe something else. If you have any proposals for structure, post 'em here. Thanks for your help! I'll be back on this next week.

greysonp avatar Feb 12 '16 19:02 greysonp

Don't hurry, relax, take your time ;).

The biggest problem with that issue is that it crashes app, that hurts. And sometimes (like in my case) you don't have a control how library calls commit method. If it's calling commitAllowingStateLoss then everything will work fine.

One idea could be also to add additional parameter in requestPermissions method and pass it to RequestData.

Can you reopen this issue, so that maybe someone else will see it and come up with better solution? I think it would be nice to fix this issue in this library, so we are independent on how and when will Google fix it.

zokipirlo avatar Feb 12 '16 22:02 zokipirlo

A related bug in AOSP is this: https://code.google.com/p/android/issues/detail?id=190966

I just found this awesome library; kudos! I hate fighting the new Permissions model, so 6ish months afgo I wrote my own similar internal library, but it was a bit more complicated than Permiso. I'm just starting the process of considering porting over to Permiso, but I use support DialogFragments quite a bit and run in to this issue in even my own code. It would be good to have an official fix for this.

paulpv avatar May 05 '16 21:05 paulpv