Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Receive incorrect resultCode in Activity's onRequestPermissionsResult when request permission from Fragment

I have an app with targetSdkVersion = 23, compileSdkVersion = 23 with main activity setup like following

- HomeActivity (AppCompatActivity)
  - FragmentA (V4 Fragment)
    - ViewPager
      - NestedFragmentA (V4 Fragment)
      - NestedFragmentB (v4 Fragment)
      - NestedFragmentC (v4 Fragment)
      - NestedFragmentD (v4 Fragment)
  - Fragment B (V4 Fragment)
  - Fragment C (V4 Fragment)

And in HomeActivity

public static final String PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    Log.i("Logger", "Request Code: " + String.valueOf(requestCode));

    // Handle permission request result
}

I read this answer from @CommonsWare,

https://stackoverflow.com/a/33170531/760363

that the onRequestPermissionsResult in nested fragment will never gets called (in my case, it result will return to HomeActivity) but I'm fine with that, I'll manually notify fragment of the result.

But the problem is, when I request for permission in HomeActivity, everything works fine.

HomeActivity

// Request permission from HomeActivity
// Supply 101 as request code, get 101 back

@Override
public void clickSomething(View v) {
    requestPermissions(new String[]{PERMISSION}, 101);
}

// Logcat
Logger: Request Code: 101 <<< CORRECT

But in FragmetnA or NestedFragmentA, when I request from nested fragment, the requestCode that return to HomeActivity has changed

FragmentA

// Request permission from FragmentA
// Supply 102 as request code, get 358 back

@Override
public void clickAnotherThing(View v) {
    requestPermissions(new String[]{HomeActivity.PERMISSION}, 102);
}

// Logcat
Logger: Request Code: 358 <<< INCORRECT

NestedFragmentA

// Request permission from NestedFragmentA
// Supply 103 as request code, get 615 back

@Override
public void clickDifferentThing(View v) {
    requestPermissions(new String[]{HomeActivity.PERMISSION}, 103);
}

// Logcat
Logger: Request Code: 615 <<< INCORRECT

Do you have any idea what would cause this issue?

like image 312
Tar_Tw45 Avatar asked Mar 23 '16 05:03

Tar_Tw45


1 Answers

I would not negate the fact that "Nested Fragments do not receive request permissions (onRequestPermissionsResult()) callback".

But what I will do here is to explain the behavior observed by you regarding the different "weird" request codes received in the container activity for the requestPermissions() made by the fragments and the nested fragments.

To explain this let's consider your example -

- HomeActivity (AppCompatActivity)
  - FragmentA (V4 Fragment)
    - ViewPager
      - NestedFragmentA (V4 Fragment)
      - NestedFragmentB (v4 Fragment)
      - NestedFragmentC (v4 Fragment)
      - NestedFragmentD (v4 Fragment)
  - Fragment B (V4 Fragment)
  - Fragment C (V4 Fragment)

Implement onRequestPermissionsResult() only in HomeActivity, FragmentA and NestedFragmentA for better understanding with logs printing the request code received

    @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            Log.d("debug", "req code :: " + requestCode);     
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

Also request certain permission from FragmentA and NestedFragmentA. Let's take exmaple of location permissions

 requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 102); 

Now whenever we requestPermissions() from fragments or nested fragments, it calls Fragment class's requestPermissions() which in turn calls FragmentHostCallback's onRequestPermissionsFromFragment() which in turn calls FragmentActivity's requestPermissionsFromFragment() . Now here lies the transformation of your request code. After validating your request code, it calls

ActivityCompat's requestPermissions()

BUT

with the transformed request code -

ActivityCompat.requestPermissions(this, permissions,((fragment.mIndex + 1) << 8) + (requestCode & 0xff));

So the changed request code is -

((fragment.mIndex + 1) << 8) + (requestCode & 0xff)

where fragment.mIndex is the fragment level . So for immediate fragment (means directly a child of the container activity), it will be "0" and for immediate nested fragment(means the fragment immediately inside a fragment) it will be "1" and it will get incremented based on how deep your fragment is nested.

In our case, for FragmentA, request code changes to

 (((0 + 1) << 8) + (102 & 0xff)) which computes to 358

And for NestedFragmentA, request code changes to

(((1 + 1) << 8) + (102 & 0xff)) which computes to 614

Now we know where the request code changes. Let's continue from ActivityCompat.requestPermissions(). So we are aware of ActivityCompat.requestPermissions() since we use this method to request permissions from activities. Also, we know this will do some operations and the user will be shown the permission popup to accept/deny the requested permission.

Now we'll come to onRequestPermissionsResult(). When the user will accept/deny then onRequestPermissionsResult() of the container activity will get invoked because ultimately ActivityCompat.requestPermissions()was called. Let's say you accept/deny the permission from FragmentA So you'll get the log-

 req code ::358

After that

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

will invoke FragmentActivity's onRequestPermissionsResult() which in turn performs some validation and call

frag.onRequestPermissionsResult(requestCode&0xff, permissions, grantResults);

Bow you can see the request code passed in frag.onRequestPermissionsResult() is different. requestCode was 358 and after &0xff it becomes 102 again. Voila !! That means although we got different request code (358) in HomeActivity's onRequestPermissionsResult(), yet we are calling FragmentA's onRequestPermissionsResult() with the original request code (102) So we will get these logs from FragmentA -

 req code ::358

Now coming to NestedFragmentA. Let's say you accept/deny the permission from NestedFragmentA So you'll get the log in HomeActivity -

 req code ::614

But we know onRequestPermissionsResult() will not be invoked for nested fragments so we won't get any logs in NestedFragmentA's onRequestPermissionsResult()

I guess I have explained the reason why we get different request codes in the container activity for the requestPermissions() made by the fragment and nested fragments.

So I'd say that for the fragments which are not nested requestPermissions() from the fragments only and implement onRequestPermissionsResult() there only and not in the container activity. For nested fragments, one should requestPermissions() for the permissions required by the nested fragments from the parent fragment only . It seems this is the only workaround.

like image 104
Shadab Ansari Avatar answered Sep 18 '22 10:09

Shadab Ansari