Italian Richiesta permessi runtime

GaNdAlF89

Active Member
Licensed User
Longtime User
Ciao a tutti!
Ho un problema nella richiesta dei permessi, cosa che faccio nell'Activity_Create richiamando la funzione Check_Permissions come segue:
B4X:
Sub Check_Permissions
    
    hasPermissions = False 'assumo che non sia stato dato nessun permesso
    
    Dim requestedPermissions As List
    requestedPermissions.Initialize
    requestedPermissions.Clear
    
    'controllo i permessi uno per uno, e se non dati, li aggiungo ad una lista
    Dim permissionResult As Boolean
    permissionResult = RuntimePermissions.Check("android.permission.WRITE_EXTERNAL_STORAGE")
    If (permissionResult = False) Then
        requestedPermissions.Add("android.permission.WRITE_EXTERNAL_STORAGE")
    End If
    permissionResult = RuntimePermissions.Check("android.permission.READ_PHONE_STATE")
    If (permissionResult = False) Then
        requestedPermissions.Add("android.permission.READ_PHONE_STATE")
    End If
    permissionResult = RuntimePermissions.Check("android.permission.CAMERA")
    If (permissionResult = False) Then
        requestedPermissions.Add("android.permission.CAMERA")
    End If
    permissionResult = RuntimePermissions.Check("android.permission.ACCESS_COARSE_LOCATION")
    If (permissionResult = False) Then
        requestedPermissions.Add("android.permission.ACCESS_COARSE_LOCATION")
    End If
    permissionResult = RuntimePermissions.Check("android.permission.ACCESS_FINE_LOCATION")
    If (permissionResult = False) Then
        requestedPermissions.Add("android.permission.ACCESS_FINE_LOCATION")
    End If
    
    If (requestedPermissions.Size > 0) Then 'ci sono permessi da richiedere, quindi hasPermissions rimane False
        Wait For (Request_Permissions(requestedPermissions)) Complete (Result As Object)
    Else
        hasPermissions = True 'permessi già dati in precedenza
    End If
    
End Sub

Sub Request_Permissions (requestedPermissions As List) As ResumableSub
    
    hasPermissions = True 'risetto il default, perchè mi serve che se anche solo un permesso non viene dato, devo fermare l'esecuzione dell'app
    
    Dim cPermission As String
    
    For p=0 To (requestedPermissions.Size-1)
        
        cPermission = requestedPermissions.Get(p)
        
        RuntimePermissions.CheckAndRequest(cPermission)
        Wait For Activity_PermissionResult (Permission As String, Result As Boolean) 'attendo il risultato
        If (Result = False) Then 'else se concesso, hasPermissions è già True di default
            hasPermissions = False
        End If
        
    Next
    
    Return Null
End Sub

Sub Activity_Create
    
    Check_Permissions
    If (hasPermissions = False) Then
      'Blocco
    End If
Ma alla prima richiesta di permesso all'utente, compare la richesta sul display ma il codice va avanti, credo di aver impostato correttamente le Wait For. Dove sbaglio? Grazie mille
 

Sagenut

Expert
Licensed User
Longtime User
Mi sembra una complicazione non necessaria.
Per richiedere più permessi puoi fare così:
B4X:
For Each permission As String In Array(rp.PERMISSION_READ_CONTACTS, rp.PERMISSION_READ_CALL_LOG, rp.PERMISSION_CALL_PHONE)
    rp.CheckAndRequest(permission)
    Wait For Activity_PermissionResult (permission As String, Result As Boolean)
    If Result = False Then          ToastMessageShow("No permission!", True)
        Activity.Finish
        Return
    End If
Next
   'we have permission!
I permessi già dati non verranno richiesti nuovamente.
Chiaramente nell'Array dovrai mettere i permessi di tuo interesse.
E se il permesso viene negato (Result = False) gestisci cosa fare.
E' comunque meglio mettere la richiesta dei singoli permessi nelle Sub dove sono necessari, subito prima delle istruzioni che ne hanno bisogno e se possibile segnalare che senza quel determinato permesso non si potrà continuare con quella funzione.
Ma se preferisci chiederli subito tutti non c'è problema.
Nella Activity_Create ci sono altre istruzioni dopo la chiamata alla Check_Permissions?
In quel caso è normale che continui.
 
Last edited:

GaNdAlF89

Active Member
Licensed User
Longtime User
Grazie per la risposta.

Nella Activity_Create ci sono altre istruzioni dopo la chiamata alla Check_Permissions?
In quel caso è normale che continui.
Perchè? Non basta che Check_Permissions faccia una Wait For su Request_Permissions, nella quale c'è la richiesta all'utente?
 

Sagenut

Expert
Licensed User
Longtime User
Il Wait For, come lo Sleep, mette in pausa la Sub che lo contiene ma restituisce il flusso al resto del programma.
Dovresti mettere un ulteriore Wait For dopo la chiamata alla Check_Permissions per attendere l'esito, se ci sono altre istruzioni subito dopo la chiamata.
 

GaNdAlF89

Active Member
Licensed User
Longtime User
Il Wait For, come lo Sleep, mette in pausa la Sub che lo contiene ma restituisce il flusso al resto del programma.
Dovresti mettere un ulteriore Wait For dopo la chiamata alla Check_Permissions per attendere l'esito, se ci sono altre istruzioni subito dopo la chiamata.
Allora il problema sta lì, ho inteso male: leggendo questo https://www.b4x.com/android/forum/t...hat-return-values-resumablesub.82670/#content ho capito che il flusso ritorna al chiamante della sub in cui si trova la Wait For, e che questo si risolve mettendo una Wait For (...) Complete (...). Ma a questo punto devo pure mettere una Wait For (...) Complete (...) nella sub dove si trova a sua volta una Wait For (...) Complete (...) ? Cioè il flusso non dovrebbe già essere bloccato sulla Wait For (Check_Permissions) Complete (...) ? Diversamente non riesco a comprendere il senso di mettere le Wait For ovunque. In quest'ultimo caso vi chiedo gentilmente di illuminarmi :) Grazie mille
 
Last edited:

Sagenut

Expert
Licensed User
Longtime User
Cioè il flusso dovrebbe già essere bloccato sulla Wait For (Check_Permissions) Complete (...)
Non trovo dove è questa linea.
 

Sagenut

Expert
Licensed User
Longtime User
Infatti il flusso dentro a Check_Permissions viene messo in pausa................ma il flusso torna alla Activity_Create che intanto può continuare.
Prova proprio a mettere
B4X:
Wait For (Check_Permissions) Complete (Result as Boolean)
in Activity_Create e far restituire True o False a seconda che i permessi siano stati concessi tutti o meno.
Però devi cambiare la tua Sub in
B4X:
Sub Check_Permissions as ResumableSub
 

GaNdAlF89

Active Member
Licensed User
Longtime User
Prova proprio a mettere...

Sì questo è chiaro, la logica è la stessa, ma...

.... il flusso torna alla Activity_Create che intanto può continuare.

... ho inteso che il flusso dentro Check_Permissions viene messo in pausa e lì si ferma dato che c'è una Complete, e non ritorna al suo chiamante cioè Activity_Create.

Da ciò che (gentilmente, e ti ringrazio molto) dici, invece mi sembra di capire che sia le Wait For semplici (senza Complete) che le Wait For Complete rimandino il flusso al chiamante. Giusto?
 

Sagenut

Expert
Licensed User
Longtime User
B4X:
Sub Activity_Create(FirstTime As Boolean)
    Wait For (Check_Permissions) Complete (Result As Boolean)
    If Result Then
        'Il programma continua
    Else
        'Il programma si chiude o avverte l'utente
    End If
End Sub

Sub Check_Permissions As ResumableSub
For Each permission As String In Array(rp.PERMISSION_READ_CONTACTS, rp.PERMISSION_READ_CALL_LOG, rp.PERMISSION_CALL_PHONE)
    rp.CheckAndRequest(permission)
    Wait For Activity_PermissionResult (permission As String, Result As Boolean)
    If Result = False Then Return False
Next
Return True
End Sub
I Wait For servono proprio ad attendere il completamento di una Sub senza bloccare l'app per evitare che vada in errore di "App Not Responding".
Se la chiamata ad una Sub esterna fosse stata già bloccante non ci sarebbe stato bisogno di inventare il Wait For. ;)
Nel tuo caso:
Activity_Create chiama la Check_Permissions.....e deve essere fatta con un Wait For se vuoi che attenda il risultato di questa prima di proseguire.
A sua volta la Check_Permissions deve fare diverse cose e ha dei Wait For per eseguirle una alla volta.
 

LucaMs

Expert
Licensed User
Longtime User
Da ciò che (gentilmente, e ti ringrazio molto) dici, invece mi sembra di capire che sia le Wait For semplici (senza Complete) che le Wait For Complete rimandino il flusso al chiamante. Giusto?
Giusto. La versione con Complete(...) si deve usare quanto la routine mandata in esecuzione contenga a sua volta uno o più Sleep o Wait For, ovvero sia una routine di "tipo resumable".
 

udg

Expert
Licensed User
Longtime User
Consentitemi un paio di riflessioni.

WaitFor(funzione)-Complete non significa "blocca il programma finchè la funzione chiamata non sia conclusa", ma va inteso come "il codice che segue il WaitFor/Complete sarà eseguito solo dopo che la funzione chiamata sarà conclusa".

Sarebbe bene non avere WaitFor/Complete in una Create perché questa dovrebbe poter terminare il più velocemente possibile (e passare a Resume).
Meglio chiamare come ultima riga della Create una funzione (Init, Inizializza, Permessi..) che al suo interno abbia uno o più WaitFor/Complete in base a ciò che occorre per completare l'inizializzazione.

Se, ad esempio, c'è da scaricare dei dati da Internet prima di essere pronti, usare tale approccio può far la differenza.

L'indicazione di @Sagenut di testare i permessi subito prima del momento in cui si rendono necessari è, a mio avviso, molto saggia. Questo perchè l'utente potrebbe rimuovere un permesso concesso allo start e quindi falsare la condizione proprio quando invece diventa necessaria. Che poi il 99.9% degli utenti non faccia nulla del genere magari è anche vero..
 

LucaMs

Expert
Licensed User
Longtime User
Sarebbe bene non avere WaitFor/Complete in una Create perché questa dovrebbe poter terminare il più velocemente possibile (e passare a Resume).
Anzi, mettere un Wait For (Complete o no) nell'Activity_Create consente l'esecuzione della Resume PRIMA che la Create stessa venga completata.

All'interno della Create, meglio usare una CallSubDelayed che mandi in esecuzione la routine di tipo resumable (ovvero contenga Sleep o Wait) e che vada eseguita prima dell'esecuzione della Resume.
 

Sagenut

Expert
Licensed User
Longtime User
Nota sulle Resumable Subs:
hanno la particolarità di poter essere chiamate più volte, ed ogni chiamata avrà il proprio inizio e fine separato dalle altre chiamate.
Questo lo puoi vedere nel Video Tutorial al riguardo dove una Resumable Sub viene chiamata subito 5 volte.
Quella Sub contiene il codice per scaricare una immagine da internet.
Le 5 immagini vengono quindi scaricate tutte insieme sfruttando una sola Sub.
Se la Sub fosse stata bloccante non si avrebbe questo beneficio.
Le Resumable Sub sono un pò ostiche all'inizio in quanto presenti solo in B4X, ma sono davvero super funzionali una volta capito bene il loro meccanismo.
 

udg

Expert
Licensed User
Longtime User
Esatto. Se metti WaitFor nella Create causi l'esecuzione della Resume prima del completamente della Create e questo potrebbe condurre ad errori di oggetti non inizializzati o altri effetti non desiderati.
Non ho mai provato con una CSD in Create per anticipare la Resume, ma resta il problema che se c'è da scaricare dati da Internet e le cose vanno per le lunghe, una Create che non termina velocemente genera problemi.
Per me è sempre meglio "sbarazzarsi" di create/resume (ovvero accelerare la loro conclusione) prima di attivare il mio codice di inizializzazione.
 

udg

Expert
Licensed User
Longtime User
Ah certo. Ormai dovrebbe essere se non vietato fortemente sconsigliato non utilizzare le B4xPages...
Ed anche le B4xView, le XUI..
Come ha scritto qualcuno di recente "dopo tanti anni dovrebbe essere chiaro che è meglio seguire l'onda" :)
 

Star-Dust

Expert
Licensed User
Longtime User
"dopo tanti anni dovrebbe essere chiaro che è meglio seguire l'Honda" :)
Non è facile , soprattutto a piedi
c2_r.png
 
Top