Android Question Email attachments

RJB

Active Member
Licensed User
There seem to be many questions regarding email attachments but I can't find anything similar to my problem so just wondering if anyone can help?

With the code below I've had to add the delay 'WaitForStatus' otherwise the attachment is sometimes sent and sometime not. Can anyone help with why that should be necessary?
Also WaitForStatus always returns the maximum value. Is it blocking the 'Sub Mail_MessageSent(Success As Boolean)'. I assumed that the 'DoEvents' would allow that to progress

Thanks

B4X:
'Class module
Sub Class_Globals
    Dim MailSender As SMTP
    Dim SendStatus As String = "Idle"
    Dim StatusTimer As Timer
End Sub

Public Sub Initialize(SendServer As String, SendPort As Int, SendUser As String, _
                                         SendPass As String, SendEvent As String)
    MailSender.Initialize(SendServer, SendPort, SendUser, SendPass, "Mail")
    StatusTimer.Initialize("StatusTimer", 25000)
    StatusTimer.Enabled = False
End Sub

Sub SendMail(MailToAddress As List, MailCCAddress As List, MailBCCAddress As List, _
                                        MailSubject As String, MailBody As String, _
                                        MailAttachmentDirectory As String, MailAttachmentFile As String) _
                                        As Boolean
                                       
    If (MailToAddress.Size = 0) _
       AND (MailCCAddress.Size = 0) _
       AND (MailBCCAddress.Size = 0) _
       Then Return False
    If MailToAddress.Size <> 0 Then
        For i = 0 To MailToAddress.Size - 1
            MailSender.To.Add(MailToAddress.Get(i))
        Next
    End If
    If MailCCAddress.Size <> 0 Then
        For i = 0 To MailCCAddress.Size - 1
            MailSender.CC.Add(MailCCAddress.Get(i))
        Next
    End If
    If MailBCCAddress.Size <> 0 Then 
        For i = 0 To MailBCCAddress.Size - 1
            MailSender.BCC.Add(MailCCAddress.Get(i))
        Next
    End If
    MailSender.Subject = MailSubject
    MailSender.Body = MailBody
    If File.exists(MailAttachmentDirectory, MailAttachmentFile) Then
        MailSender.AddAttachment(MailAttachmentDirectory, MailAttachmentFile)
    End If

    MailSender.Send
    SendStatus = "Sent"
    Log("Wait = " & WaitForStatus(2500))
    StatusTimer.Enabled = True

    Return True
End Sub

 Sub WaitForStatus(MaxMilliSeconds As Long) As Long        '4/5 seconds max?

    Dim StartTime As Long = DateTime.now
   
    Do While DateTime.Now < StartTime + MaxMilliSeconds
        If SendStatus <> "Sent" Then Return DateTime.now - StartTime
        DoEvents
    Loop
    Return MaxMilliSeconds
   
 End Sub

Sub Mail_MessageSent(Success As Boolean)
    If Success Then
        SendStatus = "OK"
        Log("mail sent ok")
    Else
        SendStatus = "Failed"
        Log("mail send failed")
    End If
    StatusTimer.Enabled = False

End Sub

Sub CheckStatus() As String
    Return SendStatus
End Sub

Sub StatusTimer_Tick
    SendStatus = "TimeOut"
    StatusTimer.Enabled = False
End Sub
 

RJB

Active Member
Licensed User
I found the answer to the question about 'DoEvents' elsewhere in this forum, it doesn't 'do' all events; "DoEvents only processes UI related messages". I've modified the StatusTimer to provide the delay that allows the mail send to work. The '.Send' is taking 1 to 10 seconds with a relatively small attachment, but perhaps that's something to do with my broadband or server?
I still don't know why the delay is necessary though, am I missing something obvious?
 

RJB

Active Member
Licensed User
Do you mean as in the original listing (i.e. using the 'waitforstatus' do while loop) or by using a timer?
The mail send with attachment doesn't seem to work without a delay. The modified statustimer sub is as shown below and seems to work, but I still don't know why its necessary! Does other code running whilst the mail send is in progress cause problems? Maybe its something to do with my slow broadband or server?
B4X:
Sub StatusTimer_Tick
    StatusCounter = StatusCounter - 1
    If StatusCounter < 0 Then
        SendStatus = "TimeOut"
        StatusTimer.Enabled = False
    Else If SendStatus <> "Sent" Then
            StatusTimer.Enabled = False
            Log((DateTime.now - StatusStartTime)/1000)
    End If
End Sub
the timer interval is set to 100mS and the counter is set to 250
 

RJB

Active Member
Licensed User
Using the program as suggested works partly. That is it sends OK with a small text attachment (16KB) and an image (285KB jpg). There is no other activity within the program at the time. In the full programme there is other activity, e.g file download, video display, etc. concurrently.
It fails when I try a small video file (8MB). There is no activity on the router after the .send as there is in the other cases and the _MessageSent sub is never called.
The mail sever can apparently take attachments up to 75MB so shouldn't be causing a problem.
 

RJB

Active Member
Licensed User
I was trying it on a Kindle(possibly not the best thing to use!) and it did show out of memory. It appeared to be trying to allocate 22MB for an 8MB file.
I then tried it on my phone which has more memory. The unfiltered log is:

"
Installing file.

PackageAdded: package:b4a.example

** Activity (main) Create, isFirst = true **

** Activity (main) Resume **

ATTACHING: /storage/emulated/0/RouteFiles/IMAG0123.MOV

ATTACHING: /storage/emulated/0/RouteFiles/IMAG0123.MOV

** Activity (main) Pause, UserClosed = false **

** Activity (main) Create, isFirst = false **

WakeLock already held.

Partial wakeLock already held.

** Activity (main) Resume **

"

which seems a bit brief! The 'ATTACHING....' message just shows that the file exists and is being attached, after which it is sent. The timer between send attempts is set to 100000mS so should allow time to send.
 

RJB

Active Member
Licensed User
For comparison sending a small text file gives:

"
Installing file.

PackageAdded: package:b4a.example

** Activity (main) Create, isFirst = true **

** Activity (main) Resume **

ATTACHING: /storage/emulated/0/RouteFiles/logfile.txt

mail sent ok

ATTACHING: /storage/emulated/0/RouteFiles/logfile.txt

mail sent ok

"
the 'mail sent ok' comes from the _MessageSent sub
if the sub had been called with Success = false then it would have said 'mail send failed' so it isn't being called when the send fails with the large file
 

RJB

Active Member
Licensed User
OK thanks. The large file thing was a bit of an aside anyway. The real problem was that I couldn't attach anything without putting a delay into the process. I've just tried it again and today it is working, even without the delay. Maybe it is something to do with my broadband/ server. Anyway I'll leave the delay in, it seems to make it more reliable and doesn't seem to cause any problems.
I seem to regularly come across the need for delays so perhaps, if others would also find it useful, something like the VB 'Threading.Thread.Sleep()' would be a useful addition to B4A?
Thanks again.
 

RJB

Active Member
Licensed User
It could be something else interacting. How can I safely wait after the mail .send until the _MessageSent sub has been called, to help prove it?
 

RJB

Active Member
Licensed User
What I was suggestion is some way to do the equivalent of:

B4X:
MailSender.Send
sendstatus = "Sent"

do while sendstatus = "Sent"
      DoEvents
      Threading.Thread.Sleep(100)
loop

Sub Mail_MessageSent(Success As Boolean)
    If Success Then
        SendStatus = "OK"
    Else
        SendStatus = "Failed"
    End If
End Sub
to make sure that nothing else can interfere before the _MessageSent sub is called. Obviously I can't just do this because a) there is no equivalent of Threading.Thread.Sleep and b) DoEvents only allows UI events to progress so _MessageSent won't be called.
 

RJB

Active Member
Licensed User
I think I've solved the conundrum using CallSubDelayed.
Returning to Main after '.send' for those activities that can continue; and handling those that are dependant on the email send completion after the 'callSubDelayed'
Thanks for your help

B4X:
............
    MailSender.Send
    SendStatus = "Sent"
    StatusTimer.Enabled = True
    Return True
End Sub

Sub Mail_MessageSent(Success As Boolean)
    If SendStatus = "TimeOut" Then Return
   
    If Success Then
        SendStatus = "OK"
        CallSubDelayed3(CallingMod, ResultSub, Action, SendStatus)
    Else
        SendStatus = "Failed"
         CallSubDelayed3(CallingMod, ResultSub, Action, SendStatus & ": " & LastException.Message)
    End If
End Sub

Sub StatusTimer_Tick
        SendStatus = "TimeOut"
        StatusTimer.Enabled = False
        CallSubDelayed3(CallingMod, ResultSub, SendStatus, SendStatus)
End Sub
 
Top