Android Question Email attachments

Discussion in 'Android Questions' started by RJB, May 12, 2015.

  1. RJB

    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

    Code:
    '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
     
  2. RJB

    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?
     
  3. Erel

    Erel Administrator Staff Member Licensed User

    You should never try to block the main thread in Android. The delay is not necessary and will only cause problems.
     
  4. RJB

    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?
    Code:
    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
     
  5. Erel

    Erel Administrator Staff Member Licensed User

    I don't see how the timer or the delay sub can affect the communication. That is unless you are somewhere closing SMTP.

    Create a new program with only the code that sends the mail with the attachments and the MessageSent sub. Does it work properly?
     
  6. RJB

    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.
     
  7. RJB

    RJB Active Member Licensed User

    Deleted - not relevant
     
  8. Erel

    Erel Administrator Staff Member Licensed User

    Check the unfiltered logs. This is probably a memory issue.
     
  9. RJB

    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.
     
  10. RJB

    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
     
  11. Erel

    Erel Administrator Staff Member Licensed User

    SMTP is a text based protocol, so binary files are converted to base 64 string and split into multiple parts.

    I don't recommend you to use SMTP for such large mails. Use FTP or HttpUtils2 instead.
     
  12. RJB

    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.
     
  13. Erel

    Erel Administrator Staff Member Licensed User

    I don't see how adding a delay to the main thread will have any effect on the thread that is actually sending the email (from inside the library).
     
  14. RJB

    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?
     
  15. Erel

    Erel Administrator Staff Member Licensed User

    Not sure what you mean with "safely wait". You don't need to do anything special.

    If the user will move your app to the background then the event will not fire (or fire when the app is restored). You can use a service if you want to handle this case.
     
  16. RJB

    RJB Active Member Licensed User

    What I was suggestion is some way to do the equivalent of:

    Code:
    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.
     
  17. Erel

    Erel Administrator Staff Member Licensed User

    As I previously wrote you cannot hold the main thread in Android.
     
  18. RJB

    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

    Code:
    ............
        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
     
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