1. *** New version of B4J is available ***
    B4J v7.8
    Dismiss Notice

Android Question Multiple Runtime Permissions / Wait For.. Complete not working? RESOLVED

Discussion in 'Android Questions' started by SimonatMSL, Jul 24, 2019.

  1. SimonatMSL

    SimonatMSL Member Licensed User

    I am trying to update an app that was based on the old Bluetooth example so may not take advantage of all the latest developments in program structure. I do not write programs for B4A very often as I mostly have to use Embedded C or design electronics. My apps are used to support our embedded systems in the field.

    This app needs to support Android 6+ Runtime permissions. The appropriate tutorial has an example of using RuntimePermissions.CheckandRequest() in conjunction with Wait For but does not illustrate setting multiples, and the technique doesn't work because my app runs into a run time error (from code foollowing the call to PermissionsTake1 that accesses the external storage) before the permissions dialog is even displayed. This is the sub PermissionsTake1.

    I tried changing to the technique using PermissionsTake2 and a resumable Sub MyPermission so I could call MyPermission with the Resumable Subs Tutorial's Wait For () Complete (Result as boolean).

    In neither case do the permissions dialogs get a chance to run but I cannot see how to restructure my program not to read the ini file in Activity_Create or shortly thereafter.

    Code:
    Sub PermissionsTake1
        
    ' 22-07-2019 access dangerous permissions for Android 6+
        Log ("Checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
        
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        
    Log ("Finished checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_WRITE_EXTERNAL_STORAGE"True)
            
    ExitApplication
        
    End If
       
        
    Log ("Checking PERMISSION_ACCESS_COARSE_LOCATION")
        rp.CheckAndRequest(rp.PERMISSION_ACCESS_COARSE_LOCATION)
        
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_COARSE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_COARSE_LOCATION"True)
            
    ExitApplication
        
    End If

        
    Log ("Checking PERMISSION_ACCESS_FINE_LOCATION")
        rp.CheckAndRequest(rp.PERMISSION_ACCESS_FINE_LOCATION)
        
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_FINE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_FINE_LOCATION"True)
            
    ExitApplication
        
    Else
            PermissionsDone = 
    True
        
    End If
       
    End Sub

    Sub PermissionsTake2
        
    Log ("Checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        
    Wait For (MyPermission(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)) Complete (Result As Boolean)
        
    Log ("Finished checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_WRITE_EXTERNAL_STORAGE"True)
            
    ExitApplication
        
    End If

        
    Log ("Checking PERMISSION_ACCESS_COARSE_LOCATION")
        
    Wait For (MyPermission(rp.PERMISSION_ACCESS_COARSE_LOCATION)) Complete (Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_COARSE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_COARSE_LOCATION"True)
            
    ExitApplication
        
    End If

        
    Log ("Checking PERMISSION_ACCESS_FINE_LOCATION")
        
    Wait For (MyPermission(rp.PERMISSION_ACCESS_FINE_LOCATION)) Complete (Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_FINE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_FINE_LOCATION"True)
            
    ExitApplication
        
    End If
       
    End Sub


    ' in theory we can make a blocking call To this by Wait For OnePermission(rp.PermName) Complete (Result As Boolean)
    Sub MyPermission(Permname As StringAs ResumableSub
        
    Log("++MyPermission")
        rp.CheckAndRequest(Permname)
        
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        
    Log("--MyPermission")
        
    Return Result

    End Sub
     

    Attached Files:

  2. Erel

    Erel Administrator Staff Member Licensed User

  3. Andrew (Digitwell)

    Andrew (Digitwell) Active Member Licensed User

    @SimonatMSL,
    I think that you have 2 problems

    1. the calling sub


    Code:
    sub domywork
       PermissionTake1
       
    'continue with other code, which crashes, because the permissions have not been set.
    end sub
    The issue is that PermissionTake1 returns to domywork as soon as the first Wait For is reached. This is expected behaviour of using Wait for

    You need to do something like:

    Code:
    sub domywork
      
    Wait for (PermissionTake1) complete (ret as object) ' or boolean or whatever
     'continue with other code, this will now wait until permissionTake1 completes
    end sub

    2. ExitApplication

    I don't believe that exitapplication closes the app immediately. I think that if there are messages on the queue these are processed.
    So the code above would still not work properly since the app does not actually exit until control is returned to the OS.

    My recommendation is this:

    Code:
    sub domywork
       PermissionTake2
       
    ' do nothing else here return to OS

    end sub

    sub continuewithapp
    ' this is where you can get on and do any work, knowing that all the permissions succeeded
    end sub

    Sub PermissionsTake2 as resumeablesub
        
    Log ("Checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        
    Wait For (MyPermission(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)) Complete (Result As Boolean)
        
    Log ("Finished checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_WRITE_EXTERNAL_STORAGE"True)
            
    ExitApplication
            
    return false
        
    End If

        
    Log ("Checking PERMISSION_ACCESS_COARSE_LOCATION")
        
    Wait For (MyPermission(rp.PERMISSION_ACCESS_COARSE_LOCATION)) Complete (Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_COARSE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_COARSE_LOCATION"True)
            
    ExitApplication 
           
    return false
        
    End If

        
    Log ("Checking PERMISSION_ACCESS_FINE_LOCATION")
        
    Wait For (MyPermission(rp.PERMISSION_ACCESS_FINE_LOCATION)) Complete (Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_FINE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_FINE_LOCATION"True)
            
    ExitApplication
            
    return false
        
    End If

        
    ' Let the app know it can continue
        callsub(me,"continuewithapp")
      
    return true
    End Sub
    I haven't checked this in the IDE.I have just typed it in here. But I think this should solve your problem. You don't actually need to return a boolean.
     
  4. SimonatMSL

    SimonatMSL Member Licensed User


    Hi

    Thanks for your suggestions but they don't work. See my Reply to Erel

    Simon
     
  5. Andrew (Digitwell)

    Andrew (Digitwell) Active Member Licensed User

    Fair enough, works for me. BTW, I can't see any other replies.
     
  6. SimonatMSL

    SimonatMSL Member Licensed User

    Thank you
    Your suggestion (2) is nice but it won'I have already read the thread (3) and tried what it suggests.

    This is the error Log now that I made some changes

    Code:
    ** Activity (main) Create, isFirst = true **
    Checking PERMISSION_WRITE_EXTERNAL_STORAGE
    ** 
    Activity (main) Resume **
    Executing Activity_Resume
    Error occurred on line: 
    60 (Common)
    java.io.FileNotFoundException: /storage/emulated/
    0/Android/data/com.msluk.net.clearwell_handheld/files/Bluetooth.ini: open failed: ENOENT (No such file or directory)
        at libcore.io.IoBridge.open(IoBridge.java:
    452)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:
    87)
        at anywheresoftware.b4a.objects.streams.File.OpenOutput(
    File.java:448)
        at anywheresoftware.b4a.objects.streams.File.WriteMap(
    File.java:290)
        at com.msluk.net.clearwell_handheld.common._loadndefaultmap(common.java:
    276)
        at com.msluk.net.clearwell_handheld.common._initmap(common.java:
    111)
        at com.msluk.net.clearwell_handheld.main._activity_resume(main.java:
    820)
        at java.lang.reflect.Method.invoke(Native Method)
        at anywheresoftware.b4a.shell.Shell.runMethod(
    Shell.java:732)
        at anywheresoftware.b4a.shell.Shell.raiseEventImpl(
    Shell.java:348)
        at anywheresoftware.b4a.shell.Shell.raiseEvent(
    Shell.java:255)
        at java.lang.reflect.Method.invoke(Native Method)
        at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:
    144)
        at anywheresoftware.b4a.BA.raiseEvent(BA.java:
    176)
        at com.msluk.net.clearwell_handheld.main.afterFirstLayout(main.java:
    110)
        at com.msluk.net.clearwell_handheld.main.access$
    000(main.java:17)
        at com.msluk.net.clearwell_handheld.main$WaitForLayout.run(main.java:
    82)
        at android.os.Handler.handleCallback(Handler.java:
    739)
        at android.os.Handler.dispatchMessage(Handler.java:
    95)
        at android.os.Looper.loop(Looper.java:
    148)
        at android.app.ActivityThread.main(ActivityThread.java:
    7406)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:
    1230)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:
    1120)
    Caused by: android.system.ErrnoException: open failed: ENOENT (No such 
    file or directory)
        at libcore.io.Posix.open(Native Method)
        at libcore.io.BlockGuardOs.open(BlockGuardOs.java:
    186)
        at libcore.io.IoBridge.open(IoBridge.java:
    438)
        ... 
    23 more
    ** 
    Activity (main) Pause, UserClosed = false **
    This is the changed code

    Code:
    Sub Activity_Create(FirstTime As Boolean)

        
    If FirstTime Then
            admin.Initialize(
    "admin")
            serial1.Initialize(
    "serial1")
            ListFiles.Initialize
            foundDevices.Initialize
            RestartTimer.Initialize(
    "RestartTimer"1000)
            RestartTimer.Enabled = 
    True
        
    End If

        
    Activity.LoadLayout("1")
        
        
    Wait for (PermissionsTake1) Complete (Result As Boolean)
        
    Log ("Returned from Permissions")

        Common.RestartApp = 
    False
        
    ' init the Map to be used like an ini file   
    '    If (FirstTime) Then
    '        Common.InitMap
    '        Common.InitAliases
    '    End If

        
    ' this button was misbehaving
        btnCancel.Enabled = True
        
        Common.KeepScreenOn(
    True)
        
    End Sub

    ' This gets called at the start and after a connect session
    Sub Activity_Resume
        
    Log("Executing Activity_Resume")
        Common.InitMap
        Common.InitAliases

        
    ' back to original code
        btnSearchForDevices.Enabled = False
        
    If admin.IsEnabled = False Then
            
    If admin.Enable = False Then
                
    ToastMessageShow("Error enabling Bluetooth adapter."True)
            
    Else
                
    ToastMessageShow("Enabling Bluetooth adapter..."False)
                
    'the StateChanged event will be soon raised
            End If
        
    Else
            Admin_StateChanged(admin.STATE_ON, 
    0)
        
    End If
        LoadLogFiles

        Common.ReadMapFromSD
        StopAnimation
        
        
    If Common.MyMap.Get(Common.mapAutoCap) Then
            Common.ScreenShot(
    Activity.Width, Activity.Height, "Main")
        
    End If
        
    End Sub

    Sub PermissionsTake1 As ResumableSub
        
    Dim PermissionsDone As Boolean = False
        
    ' 22-07-2019 access dangerous permissions for Android 6+
        Log ("Checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
        
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        
    Log ("Finished checking PERMISSION_WRITE_EXTERNAL_STORAGE")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_WRITE_EXTERNAL_STORAGE"True)
            
    ExitApplication
        
    End If
        
        
    Log ("Checking PERMISSION_ACCESS_COARSE_LOCATION")
        rp.CheckAndRequest(rp.PERMISSION_ACCESS_COARSE_LOCATION)
        
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_COARSE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_COARSE_LOCATION"True)
            
    ExitApplication
        
    End If

        
    Log ("Checking PERMISSION_ACCESS_FINE_LOCATION")
        rp.CheckAndRequest(rp.PERMISSION_ACCESS_FINE_LOCATION)
        
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        
    Log ("Finished checking PERMISSION_ACCESS_FINE_LOCATION")
        
    If Result = False Then
            
    ToastMessageShow("Unable to set up PERMISSION_ACCESS_FINE_LOCATION"True)
            
    ExitApplication
        
    Else
            PermissionsDone = 
    True
        
    End If
        
    Return PermissionsDone
    End Sub
     
  7. SimonatMSL

    SimonatMSL Member Licensed User

    Sorry I was interrupted by the postman. I have put it there now.
     
  8. Erel

    Erel Administrator Staff Member Licensed User

    Post the code based on my example that doesn't work for you.
    If possible create a small project and upload it.
     
  9. Erel

    Erel Administrator Staff Member Licensed User

    Your code is wrong. The main mistake is to try to request the permissions when your app starts instead of ALWAYS requesting them right before you need them.
    You also haven't removed the COARSE permission code. As I wrote, it is not needed.

    As you can see in the logs, Activity_Resume runs before the permission was granted. Why are you reading the file in Activity_Resume? Looks like a mistake.

    Correct code:
    Code:
    Sub Activity_Create
     ...
     
     rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
     
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
     
    If Result Then
       Common.ReadMapFromSD
     
    Else
       
    ToastMessageShow("No permission to read from external storage"True)
       
    Activity.Finish
     
    End If
     
  10. SimonatMSL

    SimonatMSL Member Licensed User

    Hi

    I just placed the code in Activity_Resume as in the example and I got this

    Code:
    ** Activity (main) Create, isFirst = true **
    ** 
    Activity (main) Resume **
    Executing Activity_Resume
    ** 
    Activity (main) Pause, UserClosed = false **
    Error occurred on line: 
    50 (Common)
    java.io.FileNotFoundException: /storage/emulated/
    0/Android/data/com.msluk.net.clearwell_handheld/files/Bluetooth.ini: open failed: ENOENT (No such file or directory)
        at libcore.io.IoBridge.open(IoBridge.java:
    452)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:
    87)
        at anywheresoftware.b4a.objects.streams.File.OpenOutput(
    File.java:448)
        at anywheresoftware.b4a.objects.streams.File.WriteMap(
    File.java:290)
        at com.msluk.net.clearwell_handheld.common._writemaptosd(common.java:
    85)
        at com.msluk.net.clearwell_handheld.main._activity_pause(main.java:
    539)
        at java.lang.reflect.Method.invoke(Native Method)
        at anywheresoftware.b4a.shell.Shell.runMethod(
    Shell.java:732)
        at anywheresoftware.b4a.shell.Shell.raiseEventImpl(
    Shell.java:348)
        at anywheresoftware.b4a.shell.Shell.raiseEvent(
    Shell.java:255)
        at java.lang.reflect.Method.invoke(Native Method)
        at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:
    144)
        at com.msluk.net.clearwell_handheld.main.onPause(main.java:
    271)
        at android.app.Activity.performPause(
    Activity.java:7061)
        at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:
    1340)
        at android.app.ActivityThread.performPauseActivity(ActivityThread.java:
    4654)
        at android.app.ActivityThread.performPauseActivity(ActivityThread.java:
    4627)
        at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:
    4602)
        at android.app.ActivityThread.access$
    1300(ActivityThread.java:229)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:
    1832)
        at android.os.Handler.dispatchMessage(Handler.java:
    102)
        at android.os.Looper.loop(Looper.java:
    148)
        at android.app.ActivityThread.main(ActivityThread.java:
    7406)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:
    1230)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:
    1120)
    Caused by: android.system.ErrnoException: open failed: ENOENT (No such 
    file or directory)
        at libcore.io.Posix.open(Native Method)
        at libcore.io.BlockGuardOs.open(BlockGuardOs.java:
    186)
        at libcore.io.IoBridge.open(IoBridge.java:
    438)
        ... 
    25 more
    The active code is now...

    Code:
    Sub Activity_Create(FirstTime As Boolean)

        
    If FirstTime Then
            admin.Initialize(
    "admin")
            serial1.Initialize(
    "serial1")
            ListFiles.Initialize
            foundDevices.Initialize
            RestartTimer.Initialize(
    "RestartTimer"1000)
            RestartTimer.Enabled = 
    True
        
    End If

        
    Activity.LoadLayout("1")
        
        
    'Wait for (PermissionsTake1) Complete (Result As Boolean)
        'Log ("Returned from Permissions")

        Common.RestartApp = 
    False
        
    ' init the Map to be used like an ini file   
    '    If (FirstTime) Then
    '        Common.InitMap
    '        Common.InitAliases
    '    End If

        
    ' this button was misbehaving
        btnCancel.Enabled = True
        
        Common.KeepScreenOn(
    True)
        
    End Sub

    ' This gets called at the start and after a connect session
    Sub Activity_Resume
        
    Log("Executing Activity_Resume")
        
        
    For Each permission As String In Array(rp.PERMISSION_WRITE_EXTERNAL_STORAGE, rp.PERMISSION_ACCESS_FINE_LOCATION)
            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

        
        Common.InitMap
        Common.InitAliases

        
    ' back to original code
        btnSearchForDevices.Enabled = False
        
    If admin.IsEnabled = False Then
            
    If admin.Enable = False Then
                
    ToastMessageShow("Error enabling Bluetooth adapter."True)
            
    Else
                
    ToastMessageShow("Enabling Bluetooth adapter..."False)
                
    'the StateChanged event will be soon raised
            End If
        
    Else
            Admin_StateChanged(admin.STATE_ON, 
    0)
        
    End If
        LoadLogFiles

        Common.ReadMapFromSD
        StopAnimation
        
        
    If Common.MyMap.Get(Common.mapAutoCap) Then
            Common.ScreenShot(
    Activity.Width, Activity.Height, "Main")
        
    End If
        
    End Sub
    I am sorry that I have not created a simpler project yet.

    As you can see I have removed it now.

    I tried your suggestion in Activity_Create but again it just ends up displaying the permissions dialog after the program has crashed due to EOENT (much the same debug Log). The program was strructured this way because of the order in which the objects are initialised (e.g. the Map for the ini file) and the file is read very early on so that the rest of the program can rely on the values. It may not be optimal but it has always worked fine and as I said, I was hoping not to have to restructure very much code because I am so busy and there is just one customer who is not using one of our company phones. I cannot understand why the Resumable Subs with Complete still seem to return right away.
     
  11. DonManfred

    DonManfred Expert Licensed User

    The code does not match the error. Or at least one can not see the problem in the code you posted.
    /files/Bluetooth.ini

    files in the files folder must be lowercased.
     
  12. Erel

    Erel Administrator Staff Member Licensed User

    I can only guess that you are still trying to access the external storage from Activity_Resume.
    Don't. Request the permission in Activity_Create and access the external storage after you get the permission.

    See the bold message here: https://www.b4x.com/android/forum/threads/handle-multiple-permission-request.94611/#post-598788
     
  13. SimonatMSL

    SimonatMSL Member Licensed User

    Okay, thanks, moving all the code to Activity_Create did almost resolve it. I also had to remove any code that accessed the filing system from Activity_Pause. I can also reliably avoid Activity_Resume or Activity_Pause from accessing filing until I set a global variable at the end of Activity_Create.

    I think I was most confused because it did not seem right to me that Activity_Resume would get triggered until after Activity_Create is 100% finished, especially when we are using the Wait For (x) Complete (y) construct.

    Thanks for your patience!
     
  14. OliverA

    OliverA Expert Licensed User

    It would probably be good to point this issue out in the "Android Process and activities life cycle" tutorial (https://www.b4x.com/android/forum/threads/android-process-and-activities-life-cycle.6487/). It makes sense that Android does not realize that the return from a Wait For is not the actual final return and therefore calls the next life cycle stage in line, even before the previous one finishes for real, but it would be good if the tutorial would make programmers aware of that.
     
    Erel and SimonatMSL like this.
  15. Erel

    Erel Administrator Staff Member Licensed User

Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice