Android Question Recommended way to display a new activity and get a return value?

JohnC

Expert
Licensed User
Longtime User
My app needs to obtain a value from the user via a new activity.

So, what is the recommended way for a sub in Activity A to display a new activity (B), activity B prompts the user for the value and returns the value when Activity B closes?

Right now, I'm using a global variable and setting it to 0, and activity B will set the variable to a value from the user, then I detect the value changed in Activity A's Activity_Resume event. But this seems like a very ugly and error prone way to do it.

So, I am open to suggestions!
 
Last edited:

KMatle

Expert
Licensed User
Longtime User
You can call a sub in Activity A from B with

B4X:
CallSubDelayed2(Activity, Paramater)

Mix it with "StartActivity"

B4X:
StartActivity (ActivityA)
CallSubDelayed2(Activity, "Sub", Paramater)

Do not finish Activity B, Just start it.
 
Upvote 0

Misterbates

Active Member
Licensed User
You can also use callsub or callsubdelayed from the second activity to raise an event in the first activity, passing back any changed value via the event.
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
You can call a sub in Activity A from B with

B4X:
CallSubDelayed2(Activity, Paramater)

Mix it with "StartActivity"

B4X:
StartActivity (ActivityA)
CallSubDelayed2(Activity, "Sub", Paramater)

Do not finish Activity B, Just start it.

But if I don't close activity B, wont activity B be displayed if the user then presses the back button while activity A is being shown because B was the last activity to be shown in the Z order?
 
Last edited:
Upvote 0

Misterbates

Active Member
Licensed User
Try raising the event (to pass the value back) then immediately after, closing activity B so that activity A ends up being shown with the return value.
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
I'll give that a try when I return to doing work on the app that has that need in a week or so.
 
Upvote 0

fredo

Well-Known Member
Licensed User
Longtime User
So, I am open to suggestions!

Here's one way to do it with a callback function
(used a lot in this app)

A testproject is attached to get an easy start

15-10-_2017_17-33-32.png

Call the other Activity via Sub EditArticleDetails() and transmit a map with all needed data:

B4X:
Sub Button1_Click
    CallSubDelayed2(Me, "EditArticleDetails", EditText1.Text)
End Sub

Sub EditArticleDetails(strId As String)
    Log("#-EditArticleDetails, strId=" & strId)
    Dim mapX As Map : mapX.Initialize
    mapX.Put("strTitle",    "Edit article with Id " & strId)
    mapX.Put("strInitText1", "Testtext Habbeldi")
    mapX.put("strId", strId)
    mapX.put("somevaluethatwillbeavailableinthecallbackfunction", 42)
    mapX.Put("CallBackSub", "EditArticleDetails_Return")
    CallSubDelayed3(Activity2, "Initialize", Me, mapX )
End Sub
'

In the other Activity fill the form from the transmittedt map:
B4X:
Sub Initialize(CallingObj As Object, mX As Map)
    mapRet         = mX
    objRet         = CallingObj
    strCallbackSub = mX.Get("CallBackSub")

    lblTitle.Text = mX.Get("strTitle")
    edtText1.Text = mX.Get("strInitText1")
End Sub

15-10-_2017_17-35-44.png

Leave the other activity via cancel or OK:
B4X:
Sub btnEsc_Click
    ' Close Activity without further actions required
    Activity.Finish
End Sub

Sub btnOk_Click
    ' Set result parameters, call the callbackfunction and close Activity
    mapRet.put("result", "OK")
    mapRet.put("edtText1", edtText1.Text)
    CallSubDelayed2(objRet, strCallbackSub, mapRet)
    Activity.Finish
End Sub
When back the transmitted map will be availabe plus extra data from the other activity:
B4X:
Sub EditArticleDetails_Return(mapResult As Map)
    Log("#-EditArticleDetails_Return")
    Log("#-  mapResult:")
    For Each k As String In mapResult.Keys
        Log($"#-    ${k} = ${mapResult.Get(k)}"$)
    Next

    Select Case mapResult.Get("result")
        Case "OK"
            lblResult.TextColor = Colors.Green       

        Case "OKLONG"
            lblResult.TextColor = Colors.Red       
   
    End Select

    lblResult.Text = $"Back from Activity2 for strId=${mapResult.Get("strId")} with the input '${mapResult.Get("edtText1")}' "$

End Sub

15-10-_2017_17-38-42.png

#-EditArticleDetails_Return
#- mapResult:
#- strTitle = Edit article with Id 12345
#- strInitText1 = Testtext Habbeldi
#- strId = 12345
#- somevaluethatwillbeavailableinthecallbackfunction = 42
#- CallBackSub = EditArticleDetails_Return
#- result = OK
#- edtText1 = Testtext Habbeldi​
 

Attachments

  • test_callactivityandreturnvalue.zip
    10.7 KB · Views: 223
Upvote 0

JohnC

Expert
Licensed User
Longtime User
OK, I see what you are doing - a manual callback.

But I have one question, in your code:

B4X:
CallSubDelayed2(objRet, strCallbackSub, mapRet)
Activity.Finish

So I am assuming CallSubDelayed2 is a "BLOCKING" call, meaning that execution will NOT continue until the CallSubDelayed2 has fully completed.

Is this right? Meaning, it is guaranteed that Activity.Finish will NOT execute until the CallSubDelayed2 completed fully.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
So I am assuming CallSubDelayed2 is a "BLOCKING" call

No. callsubdelayed put the event on the queue.
After this the activity will be finished due to your next code line.

due to the event in the queue the activity is raised and the sub is called. delayed.
 
Last edited:
Upvote 0

JohnC

Expert
Licensed User
Longtime User
That was my next question!

Thank you for the confirmation that the CallSubDelayed2 and its params are put in a que so they are not destroyed when the next line kills the activity that made the call.
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
Thanks
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
Here's one way to do it with a callback function

But, when using this method it would seem that if the user closes the new activity by pressing the phones back button (and not either of the two buttons), the original calling sub wont know anything happened.
 
Last edited:
Upvote 0

Andrew (Digitwell)

Well-Known Member
Licensed User
Longtime User
You can trap the back button in the using something similar to this:
B4X:
Sub Activity_KeyPress (KeyCode As Int) As Boolean

    Select KeyCode
               
        Case KeyCodes.KEYCODE_BACK
            btnEsc_click ' pretend the Cancel button pressed
            Return True ' consume the keypress
    End Select
    Return False
End Sub

But given all the cancel button does is close the activity i'm not sure it is necessary.
 
Upvote 0

fredo

Well-Known Member
Licensed User
Longtime User
the original calling sub wont know anything happened.

No problem.

Change in Activity2 the Sub Activity_KeyPress () to:
B4X:
Sub Activity_KeyPress (KeyCode As Int) As Boolean
    Select Case KeyCode
        Case KeyCodes.KEYCODE_BACK
            If (exitTimer.Enabled=False) Then
                ToastMessageShow("Again to exit", False)
                If (exitTimer.IsInitialized=False) Then
                    exitTimer.Initialize("exitTimer", exitTimer_tick_duration)
                End If
                exitTimer.Enabled=True
                Return True
            Else
                exitTimer.Enabled=False
               
               
                ' ------> change here to
                'Activity.Finish     
                mapRet.put("result", "ESC")
                CallSubDelayed2(objRet, strCallbackSub, mapRet)
                ' <------
               
               
                Return True
            End If
       
    End Select
    Return False
End Sub

and then
Change in Main the Sub EditArticleDetails_Return() to:
B4X:
Sub EditArticleDetails_Return(mapResult As Map)
    Log("#-EditArticleDetails_Return")
    Log("#-  mapResult:")
    For Each k As String In mapResult.Keys
        Log($"#-    ${k} = ${mapResult.Get(k)}"$)
    Next
   
    Select Case mapResult.Get("result")
        Case "ESC"
            lblResult.TextColor = Colors.yellow
            lblResult.Text = "You pressed BACK"
            Return

        Case "OK"
            lblResult.TextColor = Colors.Green           

        Case "OKLONG"
            lblResult.TextColor = Colors.Red           
       
    End Select
    lblResult.Text = $"Back from Activity2 for strId=${mapResult.Get("strId")} with the input '${mapResult.Get("edtText1")}' "$ 
   
End Sub

16-10-_2017_12-57-56.png

 
Upvote 0
Top