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
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.
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)
I hope it works fine
For Activity :
ActivityCompat.requestPermissions(this,permissionsList,REQUEST_CODE);
For Fragment :
requestPermissions(permissionsList,REQUEST_CODE);
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.
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.
FragmentActivity.onRequestPermissionsResult(..)
is where the call to fragment's onRequestPermissionsResult(..)
is forwarded to.
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);
If you are using requestPermissions in fragment, it accepts 2 parameters instead of 3.
You should use requestPermissions(permissions, PERMISSIONS_CODE);
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);
}
...
AppCompatActivity
,Should I still use super()
?
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);
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);
}
onActivityCreated()
in my code. SDK 23.
onPause()
?
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
/* 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);
This will work..
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
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);
FragmentCompat.requestPermissions()
.
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();
}
}
}
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);
}
}
}
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();
}
}
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
You can use requestPermissions(PERMISSIONS, MULTIPLE_PERMISSIONS_CODE);
. Do not use FragmentCompat if you are using v4.
Details
Kotlin 1.2.70
checked in minSdkVersion 19
Android studio 3.1.4
Algorithm
module - camera, location, ....
Check if hasSystemFeature (if module exist in phone) Check if user has access to module Send permissions request (ask user to allow module usage)
Features
Work with Activities and Fragments Has only one result response 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 -> {
// }
// }
}
}
}
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.
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)
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.
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
}
Success story sharing
Fragment.requestPermissions()
, the parent Activity is actually receiving theonRequestPermissionsResult()
. (This is with support library 23.3.0)