Ayohaych
Ayohaych

Reputation: 5189

Android M Permissions: onRequestPermissionsResult() not being called

I'm updating our app to use the new M runtime permissions system. It's all working except for onRequestPermissionsResult(). I need to check a permission on a button press, and if it's successful, send a text message. When I grant permission to do it, the dialog closes, but it doesn't trigger the Send Text until I press the button again.

I've debugged and set breakpoints in the onRequestPermissionsResult() method but it never goes into it.

This method gets called first:

    private void askForPermission() {
    String[] permissions = new String[]{Manifest.permission.SEND_SMS};
    ActivityCompat.requestPermissions(getActivity(), permissions, PERMISSIONS_CODE);
}

And then my callback looks like this:

    @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == PERMISSIONS_CODE) {
        for (int i = 0; i < permissions.length; i++) {
            String permission = permissions[i];
            int grantResult = grantResults[i];

            if (permission.equals(Manifest.permission.SEND_SMS)) {
                if (grantResult == PackageManager.PERMISSION_GRANTED) {
                    onPPSButtonPress();
                } else {
                    requestPermissions(new String[]{Manifest.permission.SEND_SMS}, PERMISSIONS_CODE);
                }
            }
        }
    }
}

Has anybody run into a similar issue? Appreciate any help with this. Thanks

Upvotes: 313

Views: 214469

Answers (26)

Aatmaj
Aatmaj

Reputation: 101

In my case, finishAffinity() was preventing onRequestPermissionsResult. It worked fine when I moved it inside the onRequestPermissionsResult.

Upvotes: 0

yavor87
yavor87

Reputation: 5929

I ran into the same issue and I just found the solution. When using the Support library, you have to use the correct method calls. For example:

  • When in AppCompatActivity, you should use ActivityCompat.requestPermissions;
  • When in android.support.v4.app.Fragment, you should use simply requestPermissions (this is an instance method of android.support.v4.app.Fragment)

If you call ActivityCompat.requestPermissions in a fragment, the onRequestPermissionsResult callback is called on the activity and not the fragment.

Upvotes: 592

Aury0n
Aury0n

Reputation: 217

for kotlin users, here my extension to check and validate permissions without override onRequestPermissionResult

 * @param permissionToValidate (request and check currently permission)
 *
 * @return recursive boolean validation callback (no need OnRequestPermissionResult)
 *
 * */
internal fun Activity.validatePermission(
    permissionToValidate: String,
    recursiveCall: (() -> Boolean) = { false }
): Boolean {
    val permission = ContextCompat.checkSelfPermission(
        this,
        permissionToValidate
    )

    if (permission != PackageManager.PERMISSION_GRANTED) {
        if (recursiveCall()) {
            return false
        }

        ActivityCompat.requestPermissions(
            this,
            arrayOf(permissionToValidate),
            110
        )
        return this.validatePermission(permissionToValidate) { true }
    }

    return true

}

Upvotes: 0

VolodymyrH
VolodymyrH

Reputation: 2019

I also met a problem that even if you call correct requestPermissions, you still can have this problem. The issue is that parent activity may override this method without calling super. Add super and it will be fixed.

Upvotes: 0

Ben.Slama.Jihed
Ben.Slama.Jihed

Reputation: 534

Answer deprecated please refer to : https://developer.android.com/training/permissions/requesting

Inside fragment, you need to call:

FragmentCompat.requestPermissions(permissionsList, RequestCode)

Not:

ActivityCompat.requestPermissions(Activity, permissionsList, RequestCode);

Upvotes: 32

grgrssll
grgrssll

Reputation: 121

UPDATE: saw someone else's answer about calling super.onRequestPermissionResult() in Activity and that fixes the request code issue I mentioned and calls the fragment's onRequestPermissionResult.

ignore this stuff:

For me it was calling the onRequestPermissionResult of the Activity despite me calling fragment.requestPermission(...), BUT it returned the result with an incorrect requestCode (111 turned into 65647 why ??? I'll never know).

Luckily this is the only permission we request on that screen so I just ignore the request code (don't have time to figure out why it's not correct right now)

Upvotes: 1

pavel
pavel

Reputation: 1701

Here i want to show my code how i managed this.

public class CheckPermission {

public Context context;

public static final int PERMISSION_REQUEST_CODE =  200;

public CheckPermission(Context context){
    this.context = context;
}

public boolean isPermissionGranted(){
    int read_contact = ContextCompat.checkSelfPermission(context.getApplicationContext() , READ_CONTACTS);
    int phone = ContextCompat.checkSelfPermission(context.getApplicationContext() , CALL_PHONE);

    return read_contact == PackageManager.PERMISSION_GRANTED && phone == PackageManager.PERMISSION_GRANTED;
   }
}

Here in this class i want to check permission granted or not. Is not then i will call permission from my MainActivity like

public void requestForPermission() {
       ActivityCompat.requestPermissions(MainActivity.this, new String[]    {READ_CONTACTS, CALL_PHONE}, PERMISSION_REQUEST_CODE);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case PERMISSION_REQUEST_CODE:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
                    showMessageOKCancel("You need to allow access to both the permissions",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                        requestPermissions(new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE},
                                                PERMISSION_REQUEST_CODE);
                                    }
                                }
                            });
                    return;
                }
            }
    }
}

Now in the onCreate method you need to call requestForPermission() function.

That's it.Also you can request multiple permission at a time.

Upvotes: 1

Ndheti
Ndheti

Reputation: 276

I had a similar problem except I was pressing a button to make a call, which triggers the callIntent. I checked permission first, if not granted I ask for permission and onRequestPermissionResult I call the check permission and call again.

 @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case Constants.PERMISSIONS_REQUEST_CALL_PHONE: {
            if ( grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                checkPermissionsAndCall();
            }
        }
    }
}

public void checkPermissionsAndCall(){
    if (Build.VERSION.SDK_INT > 22) {
        if(ContextCompat.checkSelfPermission(getContext(),
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED){
            requestPermissions( new String[]{Manifest.permission.CALL_PHONE}, Constants.PERMISSIONS_REQUEST_CALL_PHONE);
        }
        else{
            callIntent();
        }
    }
}

Upvotes: 5

Vasily  Bodnarchuk
Vasily Bodnarchuk

Reputation: 25304

Details

  • Kotlin 1.2.70
  • checked in minSdkVersion 19
  • Android studio 3.1.4

Algorithm

module - camera, location, ....

  1. Check if hasSystemFeature (if module exist in phone)
  2. Check if user has access to module
  3. Send permissions request (ask user to allow module usage)

Features

  1. Work with Activities and Fragments
  2. Has only one result response
  3. Can check several modules in one request

Solution

class PermissionType(val manifest_permission: String, val packageManager: String) {
    object Defined {
        val camera = PermissionType(Manifest.permission.CAMERA, PackageManager.FEATURE_CAMERA)
        val currentLocation = PermissionType(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.FEATURE_LOCATION_GPS)
    }
}

class  Permission {

    enum class PermissionResult {
        ACCESS_ALLOWED, ACCESS_DENIED, NO_SYSTEM_FEATURE;
    }

    interface ManagerDelegate {
        fun permissionManagerDelegate(result: Array<Pair<String, PermissionResult>>)
    }

    class Manager internal constructor(private val delegate: ManagerDelegate?) {

        private var context: Context? = null
        private var fragment: Fragment? = null
        private var activity: AppCompatActivity? = null
        private var permissionTypes: Array<PermissionType> = arrayOf()
        private val REQUEST_CODE = 999
        private val semaphore = Semaphore(1, true)
        private var result: Array<Pair<String, PermissionResult>> = arrayOf()


        constructor(permissionType: PermissionType, delegate: ManagerDelegate?): this(delegate) {
            permissionTypes = arrayOf(permissionType)
        }

        constructor(permissionTypes: Array<PermissionType>, delegate: ManagerDelegate?): this(delegate) {
            this.permissionTypes = permissionTypes
        }

        init {
            when (delegate) {
                is Fragment -> {
                    this.fragment = delegate
                    this.context = delegate.context
                }

                is AppCompatActivity -> {
                    this.activity = delegate
                    this.context = delegate
                }
            }
        }

        private fun hasSystemFeature(permissionType: PermissionType) : Boolean {
            return context?.packageManager?.hasSystemFeature(permissionType.packageManager) ?: false
        }

        private fun hasAccess(permissionType: PermissionType) : Boolean {
            return if (Build.VERSION.SDK_INT < 23) true else {
                context?.checkSelfPermission(permissionType.manifest_permission) == PackageManager.PERMISSION_GRANTED
            }
        }

        private fun sendRequest(permissionTypes: Array<String>) {

            if (fragment != null) {
                fragment?.requestPermissions(permissionTypes, REQUEST_CODE)
                return
            }

            if (activity != null){
                ActivityCompat.requestPermissions(activity!!, permissionTypes, REQUEST_CODE)
            }
        }

        fun check() {

            semaphore.acquire()
            AsyncTask.execute {
                var permissionsForSendingRequest: Array<String> = arrayOf()
                this.result = arrayOf()

                for (it in permissionTypes) {
                    if (!hasSystemFeature(it)) {
                        result += Pair(it.manifest_permission, PermissionResult.NO_SYSTEM_FEATURE)
                        continue
                    }

                    if (hasAccess(it)) {
                        result += Pair(it.manifest_permission, PermissionResult.ACCESS_ALLOWED)
                    } else {
                        permissionsForSendingRequest += it.manifest_permission
                    }
                }

                if (permissionsForSendingRequest.isNotEmpty()) {
                    sendRequest(permissionsForSendingRequest)
                } else {
                    delegate?.permissionManagerDelegate(result)
                }
            }
        }

        fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
            when (requestCode) {
                REQUEST_CODE -> {
                    if (grantResults.isEmpty()) {
                        return
                    }
                    for ((i,permission) in permissions.withIndex()) {
                        for (item in this.permissionTypes) {
                            if (permission == item.manifest_permission && i < grantResults.size) {
                                result += if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                                    Pair(item.manifest_permission, PermissionResult.ACCESS_ALLOWED)
                                } else {
                                    Pair(item.manifest_permission, PermissionResult.ACCESS_DENIED)
                                }
                                break
                            }
                        }
                    }
                    delegate?.permissionManagerDelegate(result)
                }
            }
            semaphore.release()
        }
    }
}

Usage in Activity (in fragment the same)

class BaseActivity : AppCompatActivity(), Permission.ManagerDelegate {

    private lateinit var permissionManager: Permission.Manager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.base_activity)

        permissionManager = Permission.Manager(arrayOf(PermissionType.Defined.camera, PermissionType.Defined.currentLocation), this)
        permissionManager.check()
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        permissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }


    override fun permissionManagerDelegate(result: Array<Pair<String, Permission.PermissionResult>>) {
        result.forEach {
            println("!!! ${it.first} ${it.second}")
//            when (it.second) {
//                Permission.PermissionResult.NO_SYSTEM_FEATURE -> {
//                }
//
//                Permission.PermissionResult.ACCESS_DENIED  -> {
//                }
//
//                Permission.PermissionResult.ACCESS_ALLOWED -> {
//                }
//            }
        }
    }
}

Upvotes: 1

Abhishek Dubey
Abhishek Dubey

Reputation: 945

I have an amazing solution to achieve this make a BaseActivity like this.

public class BaseActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(this));


}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    switch (requestCode) {
        case 1: {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                CustomToast.getInstance().setCustomToast("Now you can share the Hack.");

            } else {
                Toast.makeText(this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

}

Now yo can call the code to ask for permission like this

 ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);

Now every time this happens anywhere whether in your fragment or in any activity the base activity would be called.

Thanks

Upvotes: 2

havabon
havabon

Reputation: 134

Based on goodgamerguy's answer the solution is:

myFragment.this.requestPermissions(....)

Upvotes: 3

Sanjay Mangaroliya
Sanjay Mangaroliya

Reputation: 4360

I hope it works fine

For Activity :

 ActivityCompat.requestPermissions(this,permissionsList,REQUEST_CODE);

For Fragment :

 requestPermissions(permissionsList,REQUEST_CODE);

Upvotes: 69

If you are calling this code from a fragment it has it’s own requestPermissions method.

So basic concept is, If you are in an Activity, then call

ActivityCompat.requestPermissions(this,
                            permissionsList,
                            permissionscode);

and if in a Fragment, just call

requestPermissions(permissionsList,
                            permissionscode);

Upvotes: 5

Remario
Remario

Reputation: 3863

You can use requestPermissions(PERMISSIONS, MULTIPLE_PERMISSIONS_CODE);. Do not use FragmentCompat if you are using v4.

Upvotes: 1

user2002721
user2002721

Reputation: 159

private void showContacts() {
    if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS);
    } else {
        doShowContacts();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        doShowContacts();
    }
}

Upvotes: 2

Tieru
Tieru

Reputation: 1370

You can try this:

requestPermissions(permissions, PERMISSIONS_CODE);

If you are calling this code from a fragment it has it's own requestPermissions method. I believe the problem is that you are calling static method.

Pro Tip if you want the onRequestPermissionsResult() in a fragment: FragmentCompat.requestPermissions(Fragment fragment, String[] permissions, int requestCode)

Upvotes: 107

Chandler
Chandler

Reputation: 3295

Before you check everything according to above answers, make sure your request code is not 0!!!

check the code of onRequestPermissionsResult() in FragmentActivity.java:

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    int index = (requestCode>>16)&0xffff;
    if (index != 0) {
        index--;

        String who = mPendingFragmentActivityResults.get(index);
        mPendingFragmentActivityResults.remove(index);
        if (who == null) {
            Log.w(TAG, "Activity result delivered for unknown Fragment.");
            return;
        }
        Fragment frag = mFragments.findFragmentByWho(who);
        if (frag == null) {
            Log.w(TAG, "Activity result no fragment exists for who: " + who);
        } else {
            frag.onRequestPermissionsResult(requestCode&0xffff, permissions, grantResults);
        }
    }
}

Upvotes: 2

Simon Featherstone
Simon Featherstone

Reputation: 1796

I encountered this problem too. If you want the activity that handles permissions not in the history/recents, then you will be tempted to change your AndroidManifest.xml entry.

If you set the activity that you call requestPermissions or AppCompatActivity.requestPermissions with

android:noHistory="true"
android:excludeFromRecents="true"

in your AndroidManifest.xml then onRequestPermissionsResult() will not be called. This is true if your Activity is derived from Activity or AppCompatActivity.

This can be fixed by removing both flags from 'AndroidManifest.xml' and finishing your activity with finishAndRemoveTask() instead.

Upvotes: 51

Shahab Rauf
Shahab Rauf

Reputation: 3941

 /* use the object of your fragment to call the 
 * onRequestPermissionsResult in fragment after
 * in activity and use different request Code for 
 * both Activity and Fragment */

   if (isFragment)
    mFragment.requestPermissions(permissions.toArray(new
    String[permissions.size()]),requestPermission);

   else
    ActivityCompat.requestPermissions(mActivity,permissions.toArray(new
    String[permissions.size()]),requestPermission);

Upvotes: 9

almisoft
almisoft

Reputation: 2183

I have found out that is important where you call android.support.v4.app.Fragment.requestPermissions.

If you do it in onCreate(), onRequestPermissionsResult() is never called.

Solution: Call it in onActivityCreated();

@Override
public void onActivityCreated(Bundle savedInstanceState) {

    super.onActivityCreated(savedInstanceState);

    if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_DENIED) 
        requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, 0);

}

Upvotes: 12

Sumit khare
Sumit khare

Reputation: 179

This will work..

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

Upvotes: 6

TouchBoarder
TouchBoarder

Reputation: 6492

If for some reason you have extended a custom Activity located in some external library that do not call the super you will need to manually call the Fragment super.onRequestPermissionsResult yourself in your Activity onRequestPermissionsResult.

YourActivity extends SomeActivityNotCallingSuperOnRequestPermissionsResult{
Fragment requestingFragment;//the fragment requesting the permission
...
@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestingFragment!=null)
            requestingFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
...

Upvotes: 15

AkKi
AkKi

Reputation: 212

If you are using requestPermissions in fragment, it accepts 2 parameters instead of 3.

You should use requestPermissions(permissions, PERMISSIONS_CODE);

Upvotes: 15

Dusan Zivkovic
Dusan Zivkovic

Reputation: 380

If you have onRequestPermissionsResult in both activity and fragment, make sure to call super.onRequestPermissionsResult in activity. It is not required in fragment, but it is in activity.

Upvotes: 38

Ayohaych
Ayohaych

Reputation: 5189

This issue was actually being caused by NestedFragments. Basically most fragments we have extend a HostedFragment which in turn extends a CompatFragment. Having these nested fragments caused issues which eventually were solved by another developer on the project.

He was doing some low level stuff like bit switching to get this working so I'm not too sure of the actual final solution

Upvotes: 10

Marta Rodriguez
Marta Rodriguez

Reputation: 1944

You have the checkPermissions function for pre Marshmallow devices in FragmentCompat. I use like this:

FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);

Upvotes: 12

Related Questions