Spanish Permisos de tiempo de ejecución (Android 6.0 +)

desof

Well-Known Member
Licensed User
Longtime User
Esto no pretende ser un Tutorial pero si compartir mi experiecia como un humilde aporte en Español y que si pasan por un caso similar no den tan vueltas como yó para encontrar una solución viable.

PROBLEMA
El caso fue que en mi aplicación publicada en Play Store en un momento modifique el archivo manifest con estos valores <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
Y resulta que al hacer esto en versiones de Android superior a 6 (Api 23) hay que desarrollar en el código una forma de que el usuario otorgue los permisos (sólo los considerados peligrosos) de forma explicita.
Esto lo explica EREL en aquí
https://www.b4x.com/android/forum/threads/runtime-permissions-android-6-0-permissions.67689/#content
Bien entonces que quede claro que la aplicación toma los permisos del archivo manifest sólo en versiones inferior al android:targetSdkVersion="23".
Un punto importante es que por más que bajemos el android:targetSdkVersion="23"/ a una menor como >> android:targetSdkVersion="22"/ por ejemplo y subamos una nueva versión a Play Store no será autorizado y no se puede volver atrás.

SOLUCIÓN
La solución está en el enlace del vinculo superior que comaprti de EREL pero en mi caso como tengo muchas dificultades en entender las traducciones automáticas del navegador no interpretaba del todo el concepto asi que fui mejorando hasta que el código al final funcionó correctamente.
No es demasiado complicado y seguro que se puede optimizar pero lo comparto tan como lo utilizo..
En mi caso mi app utilizaba 2 permisos considerados como peligrosos que son:

WRITE_EXTERNAL_STORAGE y CALL_PHONE.

Asi que cuando un usuario en un dispositivo con una version de Android 6 o superior intenta hacer un llamado presionando el Botón Lllamar (SendCall) se produce un error y se le brinda un mensaje y se llama a rp.CheckAndRequest(rp.PERMISSION_CALL_PHONE) para que llame al diálogo de solicitar la autorización correspondiente.

B4X:
Sub SendCall(PhoneNumber As String)
    Dim p As PhoneCalls
 
    Try     
        StartActivity(p.Call(PhoneNumber))     
    Catch
        Msgbox("Para poder realizar llamadas debe autorizar permisos en el siguiente diálogo.", "Solicitud de Permisos")
        rp.CheckAndRequest(rp.PERMISSION_CALL_PHONE)
    End Try

End Sub

Sub Activity_PermissionResult (Permission As String, Result As Boolean)
    Log($"Permission: ${Permission}
Result: ${Result}"$)

 
    Select Permission
        Case rp.PERMISSION_READ_PHONE_STATE
            If Result Then rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
        Case rp.PERMISSION_WRITE_EXTERNAL_STORAGE
            If Result Then rp.CheckAndRequest(rp.PERMISSION_CALL_PHONE)
        Case rp.PERMISSION_CALL_PHONE
            If Result Then rp.CheckAndRequest(rp.PERMISSION_ACCESS_FINE_LOCATION)
        Case rp.PERMISSION_ACCESS_FINE_LOCATION
            If Result Then rp.CheckAndRequest(rp.PERMISSION_READ_EXTERNAL_STORAGE)
        Case rp.PERMISSION_READ_EXTERNAL_STORAGE
            If Result Then rp.CheckAndRequest(rp.PERMISSION_RECORD_AUDIO)
        Case rp.PERMISSION_RECORD_AUDIO
            '
    End Select
End Sub

Si tu aplicación necesita otros permisos al desatarse el Evento Activity_PermissionResult tambien serán solicitados los otros (como en mi caso el usuario deber habilitar los 2 de los diálogos que lo solicitan)

Bien ojalá sirva de algo este aporte por todo lo que a mi me ayudan los capos de este Foro !!
 

bgsoft

Well-Known Member
Licensed User
Longtime User
Hola

Antes de nada darte las gracias por el aporte.

Hay una parte del código que no entiendo:
B4X:
Select Permission
Case rp.PERMISSION_WRITE_EXTERNAL_STORAGE
If Result Then rp.CheckAndRequest(rp.PERMISSION_CALL_PHONE)
Si tienes permiso para escribir, por que pides permiso para llamar? , bueno, tampoco me hagas mucho caso, formará parte de tu código.

Particularmente pido todos los permisos "peligrosos" en el Create, y luego desde donde los voy a emplear consulto el permiso, ten en cuenta que el evento se produce en el activity (Activity_PermissionResult), pero tu puedes consultar si tienes el permiso concedido desde cualquier modulo, servicio, etc empleando esto:

B4X:
Dim rp As RuntimePermissions
If rp.Check(rp.PERMISSION_READ_EXTERNAL_STORAGE) = False Then 
  ' el permiso no está concedido
End If


También puedes darle mas velocidad al código si miras antes que versión de Api tiene el dispositivo:

B4X:
Sub Process_Globals
Public FlagAndroid6oSuperior = False  As Boolean
...
...


Sub Activity_Create(FirstTime As Boolean)
   
    Dim p As Phone
    Dim va As Int
    va = p.SdkVersion
    If va >= 23 Then 
      FlagAndroid6oSuperior = True
    Else
        FlagAndroid6oSuperior = False
    End If        

...
...

Luego puedes consultarlo desde cualquier módulo el resultado, NO EL EVENTO del resultado

B4X:
  If FlagAndroid6oSuperior and rp.Check(rp.PERMISSION_ACCESS_COARSE_LOCATION) = False Then
      ' Android >= 6 y no hay permiso 
  End If

Saludos
 

desof

Well-Known Member
Licensed User
Longtime User
Muchas gracias por tus concejos soy bastante nuevo es esto y limitado por eso aclare que se podría mejorar.
Respecto a tu duda de
Select PermissionCase rp.PERMISSION_WRITE_EXTERNAL_STORAGEIf Result Then rp.CheckAndRequest(rp.PERMISSION_CALL_PHONE)
la verdad es que esa función la copie de un hilo y veo que es genérica para todos los permisos y me resulto adecuada aunque no comprenda mucho exactamente lo que hace.
Muy bueno tu aporte no sabía esos de comprobar la versión y tal vez si hubiese comenzado mi búsqueda de este problema en este foro en Español me ahorraba unos dolores de cabeza,.
 

bgsoft

Well-Known Member
Licensed User
Longtime User
Hola

Muy facil de entender y así sabras mejor que hacer y eliminar codigo que no te sirve para nada, te pongo a grandes rasgos lo que es cada cosa, en el ide si te pones encima te amplia esto que te digo.


B4X:
rp.Check(rp.PERMISSION_READ_EXTERNAL_STORAGE) 
' comprueba el permiso, te devuelve True o False. Lo puedes hacer desde donde quieras (activiy, servicio,etc)

rp.CheckAndRequest(rp.PERMISSION_READ_EXTERNAL_STORAGE) 
' chequea el permiso y en caso que no exista se lo pide al usuario, y el resultado lo tendras en el evento Activity_PermissionResult , este método solo lo puedes hacer desde un activity


Sub Activity_PermissionResult (Permission As String, Result As Boolean)
' entra cuando tu pides un permiso y el usuario acepta o deniega
' En Permission está el nombre del permiso que has pedido, por ejemplo rp.PERMISSION_READ_EXTERNAL_STORAGE
' Result te dice con true o false SI el usuario te ha permitido el permiso, o sea, le ha dado a aceptar

Ahora que ves que no es algo complejo, solo tienes que implementarlo.
Una cosa importante, no te compliques la vida en guardarte que permisos tienes concedido, eso ya lo hace Android, y cuando tu consultes que permiso tienes te lo dirá, es mas, si tu App tenia permisos en un sdk inferior y al pasarla a la que pide permisos esos permisos ya estaban concedidos los mantendrá. Esos permisos los puedes ver en tu dispositivo movil en Ajustes/Privacidad y seguridad/Permisos de aplicacion (esto varia algo dependiendo de versiones)

Yo te recomiendo que solo pidas los permisos que Google define como "peligrosos" , si no lo son no lo pidas, no tiene sentido.

Saludos
 

desof

Well-Known Member
Licensed User
Longtime User
Entiendo !
Y me puedes explicar por que me cierra la aplicacion este trozo de código que yo utilizaba y me da error y la linea que lo provoca es
rp.Check(rp.PERMISSION_READ_EXTERNAL_STORAGE)
o
rp.Check(rp.PERMISSION_CALL_PHONE)

B4X:
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout1")
End Sub

Sub Button1_Click
    'rp.Check(rp.PERMISSION_READ_EXTERNAL_STORAGE) 
    rp.CheckAndRequest(rp.PERMISSION_CALL_PHONE)
End Sub

Sub Activity_PermissionResult (Permission As String, Result As Boolean)
    If Permission = rp.PERMISSION_CALL_PHONE Then
    '    gmap.MyLocationEnabled = Result
    End If
End Sub

sucede esté o no este en el manifest
AddManifestText(
<uses-permission
android:name="android.permission.PERMISSION_CALL_PHONE"
android:maxSdkVersion="18" />
)
 
Last edited:

bgsoft

Well-Known Member
Licensed User
Longtime User
Hola

Saber un error sin ver el código es dificil, me llamo Jesús pero los milagros me cuestan :D (es broma)

Para hacer las peticiones de servicio, el sdk máximo (targetSdkVersion) tiene que estar a 23 (como mínimo), con el sdk 23 sale la version de Android 6.0 que es la que ya pide permisos de esa forma, por lo tanto tu linea donde determinas que apis vas a utilizar seria algo asi:
B4X:
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23"/>

Acuérdate de cambiar en el IDE el camino y ponerle en android.jar la sdk que utilizas.

Segunda cuestión; si vas a utilizar algo que requiera permisos, idependientemente de que sean peligrosos o no y android te obligue a consultar con el usuario, tendrás que añadirlo en el manifest, y si lo que has copiado del manifest es solo eso, pues otro motivo (ademas del primero) para que te de error, por que falta el permiso de lectura AddPermission(android.permission.READ_EXTERNAL_STORAGE)


Te escribo algunos permisos por si los necesitas para que no los tengas que buscar.

B4X:
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.READ_PHONE_STATE)
AddPermission(android.permission.READ_EXTERNAL_STORAGE)
AddPermission(android.permission.WRITE_EXTERNAL_STORAGE)
AddPermission(android.permission.ACCESS_FINE_LOCATION)
AddPermission(android.permission.ACCESS_LOCATION_EXTRA_COMMANDS)
AddPermission(android.permission.ACCESS_COARSE_LOCATION)

Saludos
 

desof

Well-Known Member
Licensed User
Longtime User
Hola

Saber un error sin ver el código es dificil, me llamo Jesús pero los milagros me cuestan :D (es broma)

Para hacer las peticiones de servicio, el sdk máximo (targetSdkVersion) tiene que estar a 23 (como mínimo), con el sdk 23 sale la version de Android 6.0 que es la que ya pide permisos de esa forma, por lo tanto tu linea donde determinas que apis vas a utilizar seria algo asi:
B4X:
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23"/>

Acuérdate de cambiar en el IDE el camino y ponerle en android.jar la sdk que utilizas.

Segunda cuestión; si vas a utilizar algo que requiera permisos, idependientemente de que sean peligrosos o no y android te obligue a consultar con el usuario, tendrás que añadirlo en el manifest, y si lo que has copiado del manifest es solo eso, pues otro motivo (ademas del primero) para que te de error, por que falta el permiso de lectura AddPermission(android.permission.READ_EXTERNAL_STORAGE)


Te escribo algunos permisos por si los necesitas para que no los tengas que buscar.

B4X:
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.READ_PHONE_STATE)
AddPermission(android.permission.READ_EXTERNAL_STORAGE)
AddPermission(android.permission.WRITE_EXTERNAL_STORAGE)
AddPermission(android.permission.ACCESS_FINE_LOCATION)
AddPermission(android.permission.ACCESS_LOCATION_EXTRA_COMMANDS)
AddPermission(android.permission.ACCESS_COARSE_LOCATION)

Saludos

Mira en este simple proyecto que hice para emular el error podes ver el codigo completo en la captura

zaQMtNwnP.png


}Si queres subo el proyecto.

PERO en Modo Debug si sale la ventana que me pide los permisos pero en modo Release nó sólo se cierra!
 

bgsoft

Well-Known Member
Licensed User
Longtime User
Hola Desof

Varias cosas, te dije:
Segunda cuestión; si vas a utilizar algo que requiera permisos, idependientemente de que sean peligrosos o no y android te obligue a consultar con el usuario, tendrás que añadirlo en el manifest, y si lo que has copiado del manifest es solo eso, pues otro motivo (ademas del primero) para que te de error, por que falta el permiso de lectura AddPermission(android.permission.READ_EXTERNAL_STORAGE)

En tu programa pides permiso para leer, pero en el manifest no pones
AddPermission(android.permission.READ_EXTERNAL_STORAGE) , asi que por ahí te podrá dar un error (cuando vayas a acceder a leer).

En tu programa creas la variable FlagAndroid6oSuperior y ademas consultas la Api, pero no la utilizas por que despúes en el create pones directamente rp.CheckAndRequest(rp.PERMISSION_READ_EXTERNAL_STORAGE) , por lo tanto no estas filtrando dispositivos que no tengan Android 6, esto no es ningún problema, pero tienes una consulta que no utilizas y una variable que tampoco.
Seria tan facil como poner:
B4X:
If FlagAndroid6oSuperior Then rp.CheckAndRequest(rp.PERMISSION_READ_EXTERNAL_STORAGE)


Si tu estas en un dispositivo le pides permiso de lectura, y le confirmas que si, cuando vuelvas a entrar en esa APP no te lo pedirá, por que como te dije:

Una cosa importante, no te compliques la vida en guardarte que permisos tienes concedido, eso ya lo hace Android, y cuando tu consultes que permiso tienes te lo dirá, es mas, si tu App tenia permisos en un sdk inferior y al pasarla a la que pide permisos esos permisos ya estaban concedidos los mantendrá. Esos permisos los puedes ver en tu dispositivo movil en Ajustes/Privacidad y seguridad/Permisos de aplicacion (esto varia algo dependiendo de versiones)

Por tanto, no busques que cada vez que ejecutes el programa te pida permisos si ya los has concedido. Si quieres probarlo, ves donde te dije (Ajustes/Privacidad y seguridad/Permisos de aplicacion (esto varia algo dependiendo de versiones) ) y quita los permisos, asi cuando vuelvas a entrar te los volverá a pedir. Y si no, densinstala la App .

Por otro lado, te aconsejo que compiles en modo debug cuando tengas un problema y quieras saber donde te da el error, por experiencia te diré que el uso continuado del debug con cambios y luego salvando para que acepte los cambios al final los programas hacen maravillas, desde cambiar colores, cambiar tamaños, etc . Supongo que será un problema de la gestion de memoria de Windows y de Android, por eso solo lo empleo cuando es necesario, normalmente compilo en release que al fin y al cabo es como va a funcionar la App final.

Si lees detenidamente mis respuestas verás cual es la solución a los problemas que estas teniendo.

Saludos
 

desof

Well-Known Member
Licensed User
Longtime User
Muchisimas pero muchisimas GRACIAS amigo!
por tu extensa y clara explicación el error era definitivamente en el Manifest como bien me dijiste tu lo que sucede que yo colocaba este AddPermission(android.permission.PERMISSION_CALL_PHONE) y era este AddPermission(android.permission.READ_EXTERNAL_STORAGE).
Respecto a lo otro que me comentas que no uso la variable es por que solo arme un proyecto aparte para reducirlo y mostrarlo. Pero me quedo bien clara
tu explicación y he aprendido cosas importantes como por ejemplo ir al teléfono y deshabilitar los permisos para no tener que desinstalarlo y seguir practicando.

:)
 
  • Like
Reactions: eps
Top