Permisos de Android M: no se llama a onRequestPermissionsResult ()

291

Estoy actualizando nuestra aplicación para usar el nuevo sistema de permisos de tiempo de ejecución M. Todo funciona a excepción de onRequestPermissionsResult (). Necesito verificar un permiso al presionar un botón y, si tiene éxito, enviar un mensaje de texto. Cuando otorgo permiso para hacerlo, el cuadro de diálogo se cierra, pero no activa el envío de texto hasta que vuelva a presionar el botón.

He depurado y establecido puntos de interrupción en el método onRequestPermissionsResult () pero nunca entra en él.

Este método se llama primero:

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

Y luego mi devolución de llamada se ve así:

    @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);
                }
            }
        }
    }
}

¿Alguien se ha encontrado con un problema similar? Agradezco cualquier ayuda con esto. Gracias

AndyOHart
fuente

Respuestas:

576

Me encontré con el mismo problema y acabo de encontrar la solución. Al usar la biblioteca de soporte, debe usar las llamadas de método correctas. Por ejemplo:

  • Cuando esté en AppCompatActivity , debe usar ActivityCompat.requestPermissions ;
  • Cuando esté en android.support.v4.app.Fragment , debe usar simplemente requestPermissions (este es un método de instancia de android.support.v4.app.Fragment)

Si llama a ActivityCompat.requestPermissions en un fragmento, la devolución de llamada onRequestPermissionsResult se llama a la actividad y no al fragmento.

¡Espero que esto ayude!

yavor87
fuente
13
También ocurre sin actividades de Historia: code.google.com/p/android-developer-preview/issues/…
Anthony Bobenrieth
11
"Cuando esté en android.support.v4.app.Fragment, debería usar simplemente requestPermissions" - ¡gracias!
Tigran Sarkisian
55
@AnthonyBobenrieth, ¿cuál es la solución aquí para las actividades de noHistory? ¿Simplemente no podemos solicitarles permisos?
Dean Wild
2
El enlace está roto @AnthonyBobenrieth. ¿Hay alguna solución para no tener historia?
fersarr
3
Cuando se usa Fragment.requestPermissions(), la actividad principal realmente está recibiendo el onRequestPermissionsResult(). (Esto es con la biblioteca de soporte 23.3.0)
Someone Somewhere
101

Puedes probar esto:

requestPermissions(permissions, PERMISSIONS_CODE);

Si está llamando a este código desde un fragmento, tiene su propio método requestPermissions. Creo que el problema es que está llamando al método estático.

Consejo profesional si quieres que esté onRequestPermissionsResult()en un fragmento: FragmentCompat.requestPermissions(Fragment fragment, String[] permissions, int requestCode)

Tieru
fuente
Gracias por la ayuda. Me di cuenta de que hay un método requestPermissions si está en un fragmento. Estoy en un fragmento e intenté llamar solo requestPermissions, desafortunadamente no funcionó. También probé lo que mencionaste y tampoco funcionó.
AndyOHart
Esto funcionó para mí en Android M. ¿Pero me pregunto si podría causar algún problema en los dispositivos Pre-M?
Bala Vishnu
44
@goodgamerguy, asegúrese de no usar el código de solicitud en onRequestPermissionsResult of activity. si su actividad también utiliza la devolución de llamada onRequestPermissionsResult, debe proporcionar el valor predeterminado: super.onRequestPermissionsResult (requestCode, permissions, grantResults);
Timmy Simons
68

Espero que funcione bien

Para la actividad:

 ActivityCompat.requestPermissions(this,permissionsList,REQUEST_CODE);

Para el fragmento:

 requestPermissions(permissionsList,REQUEST_CODE);
Sanjay Mangaroliya
fuente
¿Qué pasa con SDK 15 y versiones inferiores?
zulkarnain shah
44
No necesita permiso sobre SDK 15, solo Marshmallow y la versión anterior necesitan ...
Sanjay Mangaroliya
50

También encontré este problema. Si desea que la actividad que maneja permisos que no están en el historial / recientes, se verá tentado a cambiar su AndroidManifest.xmlentrada.

Si configura la actividad a la que llama requestPermissionso AppCompatActivity.requestPermissionscon

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

en su AndroidManifest.xmlentonces onRequestPermissionsResult()no será llamado. Esto es cierto si su Actividad se deriva de Activityo AppCompatActivity.

Esto se puede solucionar eliminando ambos indicadores de 'AndroidManifest.xml' y finalizando con su actividad finishAndRemoveTask().

Simon Featherstone
fuente
Hubiera preferido no tener que eliminar el atributo noHistory, pero esta fue la solución que funcionó para mí. ¡Gracias!
Eric Schlenz
1
Tuve el problema con Marshmallow pero no con Nougat, gracias, funcionó para mí
Yohan Dahmani
37

Si tiene onRequestPermissionsResultactividad y fragmento, asegúrese de llamar super.onRequestPermissionsResulta la actividad. No se requiere en fragmentos, pero está en actividad.

Dusan Zivkovic
fuente
Además, tenga en cuenta que si apunta a una API (como 19, por ejemplo), seguirá estando bajo el antiguo sistema de modelo de permiso y, por lo tanto, no recibirá las llamadas de función relacionadas con el permiso de solicitud
Bonatti,
mira mi respuesta si has extendido otra actividad que no llama super
TouchBoarder
3
Tropecé con este. FragmentActivity.onRequestPermissionsResult(..)es donde onRequestPermissionsResult(..)se reenvía la llamada al fragmento .
JanPl
@Bonatti Entonces, si trato de solicitar un permiso en Android 4.4.2, ¿no obtendré un resultado? Por lo tanto, no puedo cambiar los permisos, ¿solo verlos?
Alex Fragotsis
@AlexanderFragotsis Obtendrás un resultado. La única diferencia es que no se le pedirá que otorgue permiso, sino que el sistema lo otorgará automáticamente.
Dusan Zivkovic
33

Respuesta obsoleta, consulte: https://developer.android.com/training/permissions/requesting

Dentro del fragmento, debe llamar:

FragmentCompat.requestPermissions(permissionsList, RequestCode)

No:

ActivityCompat.requestPermissions(Activity, permissionsList, RequestCode);
Ben.Slama.Jihed
fuente
2
Se relaciona con android.support.v13.app.FragmentCompat
Udi Oshi
1
FragmentCompat está en desuso a partir de API 27.1.0.
Big McLargeHuge
mi respuesta está en desuso, consulte: developer.android.com/training/permissions/requesting
Ben.Slama.Jihed
15

Si está utilizando requestPermissions en fragmentos, acepta 2 parámetros en lugar de 3.

Deberías usar requestPermissions(permissions, PERMISSIONS_CODE);

AkKi
fuente
1
En realidad, proporciona una respuesta a la pregunta. Al llamar a requestPermissions (método de instancia de fragmento) en lugar de ActivityCompat.requestPermissions, se activa el método de resultado. Cuál es el problema principal aquí. Gracias Don
Waclock
15

Si por alguna razón ha extendido una Actividad personalizada ubicada en una biblioteca externa que no llama al super, deberá llamar manualmente al Fragmento super.onRequestPermissionsResult usted mismo en su 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);
    }
...
TouchBoarder
fuente
12

Tiene la función checkPermissions para dispositivos pre Marshmallow en FragmentCompat . Yo uso así:

FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
Marta Rodriguez
fuente
2
¡Esto solo funciona si se solicita el permiso dentro de android.app.fragment, y tiene que importar la biblioteca support-v13!
MohammadReza
44
FragmentCompat.requestPermissions () no funciona con android.support.v4.app.Fragment. Alguien sabe cómo manejar esto.
Jutikorn
12

He descubierto que es importante donde llamas android.support.v4.app.Fragment.requestPermissions.

Si lo haces en onCreate(),onRequestPermissionsResult() nunca se llama.

Solución: llámalo 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);

}
almisoft
fuente
1
Estoy enfrentando el mismo problema pero no puedo ingresar onActivityCreated()mi código. SDK 23.
kAmol
No estoy fragmentado, estoy tratando de hacerlo en Crear una actividad
kAmol
Hmm, o uso onPause()?
almisoft
10

Este problema fue causado realmente por NestedFragments. Básicamente, en la mayoría de los fragmentos hemos extendido un HostedFragment que a su vez extiende un CompatFragment. Tener estos fragmentos anidados causó problemas que finalmente fueron resueltos por otro desarrollador en el proyecto.

Estaba haciendo algunas cosas de bajo nivel, como el cambio de bits para que esto funcione, así que no estoy muy seguro de la solución final real

AndyOHart
fuente
Esto me ayudó a resolver mi problema: tuve que redirigir onRequestPermissionsResult desde el fragmento primario al fragmento secundario (anidado). ¡Gracias por el consejo!
Murphy
9
 /* 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);
Shahab Rauf
fuente
6

Esto funcionará ...

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
Sumit khare
fuente
la súper implementación llama a la implementación de fragmentos no el marco
Mohammad Yahia
5

Si está llamando a este código desde un fragmento, tiene su propio método requestPermissions.

Entonces, el concepto básico es: si está en una actividad, llame al

ActivityCompat.requestPermissions(this,
                            permissionsList,
                            permissionscode);

y si está en un fragmento, solo llame

requestPermissions(permissionsList,
                            permissionscode);
PounKumar Purushothaman
fuente
1
Requiere API nivel 23 ... En los anteriores, use FragmentCompat.requestPermissions().
Minas Mina
5

Tuve un problema similar, excepto que estaba presionando un botón para hacer una llamada, lo que activa el callIntent. Primero verifiqué el permiso, si no me lo otorgan, pido permiso y en RequestPermissionResult llamo al permiso de verificación y llamo nuevamente.

 @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();
        }
    }
}
Ndheti
fuente
2

¡Antes de verificar todo de acuerdo con las respuestas anteriores, asegúrese de que su código de solicitud no sea 0!

verifique el código de onRequestPermissionsResult () en 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);
        }
    }
}
Velero
fuente
2
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();
    }
}
usuario2002721
fuente
2

Tengo una solución increíble para lograr esto, hacer una BaseActivity como esta.

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();
            }
        }
    }
}

}

Ahora puedes llamar al código para pedir un permiso como este

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

Ahora, cada vez que esto sucede en cualquier parte, ya sea en su fragmento o en cualquier actividad, la actividad base se llamaría.

Gracias

Abhishek Dubey
fuente
1

Puedes usar requestPermissions(PERMISSIONS, MULTIPLE_PERMISSIONS_CODE);. No use FragmentCompat si está usando v4.

Remario
fuente
Por favor, explique por qué.
Colocable
1

Detalles

  • Kotlin 1.2.70
  • verificado en minSdkVersion 19
  • Android studio 3.1.4

Algoritmo

módulo - cámara, ubicación, ....

  1. Compruebe si hasSystemFeature (si módulo existe un en el teléfono)
  2. Compruebe si el usuario tiene acceso a módulo
  3. Enviar solicitud de permisos (pedir al usuario que permita el uso del módulo )

Caracteristicas

  1. Trabajar con actividades y fragmentos
  2. Solo tiene una respuesta de resultado
  3. Puede verificar varios módulos en una solicitud

Solución

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()
        }
    }
}

Uso en la actividad (en fragmento igual)

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 -> {
//                }
//            }
        }
    }
}
Vasily Bodnarchuk
fuente
1

Aquí quiero mostrar mi código cómo logré esto.

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;
   }
}

Aquí en esta clase quiero verificar el permiso otorgado o no. Es entonces cuando llamaré al permiso de mi MainActivity como

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;
                }
            }
    }
}

Ahora, en el método onCreate, debe llamar a la función requestForPermission ().

Eso es todo, también puedes solicitar permisos múltiples a la vez.

pavel
fuente
1

ACTUALIZACIÓN: vi la respuesta de otra persona sobre llamar super.onRequestPermissionResult()a Activity y eso soluciona el problema del código de solicitud que mencioné y llama al fragmento onRequestPermissionResult del fragmento.

ignora estas cosas:

Para mí estaba llamando a onRequestPermissionResultla Actividad a pesar de que yo llamabafragment.requestPermission(...) , PERO devolvió el resultado con un código de solicitud incorrecto (111 se convirtió en 65647 ¿por qué? Nunca lo sabré).

Afortunadamente, este es el único permiso que solicitamos en esa pantalla, así que simplemente ignoro el código de solicitud (no tengo tiempo para descubrir por qué no es correcto en este momento)

Grgrssll
fuente
Agregue 65536 y 111 y lo obtendrá. Esto se debe a las máscaras de bits. Ver algunos temas.
CoolMind
0

También me encontré con un problema que, incluso si llama a requestPermissions correcto, aún puede tener este problema. El problema es que la actividad principal puede anular este método sin llamar a super. Agregue super y se solucionará.

VolodymyrH
fuente