Android Code Snippet Correct Intent to compose an email on Android

Discussion in 'Code Snippets' started by ivan.tellez, Oct 9, 2014.

  1. ivan.tellez

    ivan.tellez Active Member Licensed User

    I had to add an "Send feedback" an my app, there are lots of examples in the forum to do this, but, all of them are wrong.

    The main problem is that they dont create a TRUE email intent, so, many other apps (NO mail clients) are shown on the IntentChooser, like Google drive, Telegram, etc.


    Its is bad if you put a "Send email" button aon your app and the user is taken to his Google Drive.


    Wrong Code:
    Code:
    Dim Message As Email
        
    Dim intent1 As Intent
        Message.To.Add(
    "example@outlook.com")
        Message.Subject = 
    "Feedback" 
        intent1 = Message.GetIntent
        
    StartActivity(intent1)
    Incorrect.jpg





    Correct Code:
    Code:
    Dim intent1 As Intent
        intent1.Initialize(
    "android.intent.action.SENDTO""mailto:example@outlook.com")
        intent1.putExtra(
    "android.intent.extra.SUBJECT""This is the subject")
        intent1.putExtra(
    "android.intent.extra.TEXT""This is the message body")
        intent1.WrapAsIntentChooser(
    "Send feedback")
        
    StartActivity(intent1)
    Correct.jpg

    Using this metod, will show only mail apps in the IntentChooser.



    Please like it this was helpfull to you
     
    Last edited: Oct 9, 2014
    yiankos1, Devan, Dave O and 2 others like this.
  2. Douglas Farias

    Douglas Farias Expert Licensed User

    only for question
    if the android dont have mail apps? this code crash?
     
  3. ivan.tellez

    ivan.tellez Active Member Licensed User

    NO.


    If you use the intent chooser (intent1.WrapAsIntentChooser("Send feedback")) It will display the same dialog as in the picture of the "Correct Code", but with a text warning that there are not apps to perform the action. The downside, is that your app cant tell if there are mail apps.

    But if you delete the WrapAsIntentChooser, the app will crash if there are no email apps. You can use this:


    Code:
    Try
        
    Dim intent1 As Intent
        intent1.Initialize(
    "android.intent.action.SENDTO""mailto:example@outlook.com")
        intent1.putExtra(
    "android.intent.extra.SUBJECT""This is the subject")
        intent1.putExtra(
    "android.intent.extra.TEXT""This is the message body")
        
    StartActivity(intent1)
    Catch
        
    ToastMessageShow("No mail apps"True)
    End Try
     
    Douglas Farias likes this.
  4. Kevin

    Kevin Well-Known Member Licensed User

    Interesting! Can we add an attachment using this method?
     
  5. Kevin

    Kevin Well-Known Member Licensed User

    I've been trying to attach a text file but I just can't seem to do it. The file is stored in ExternalStorage.

    Using the original ('wrong') method, the attachment works fine but not with the new method. Anyone have any ideas?

    Code:
    SendAtt = File.DirRootExternal & "/DirecTV_Remote/DirecTVRemote_ini.txt"

    Dim intent1 As Intent
    intent1.Initialize(
    "android.intent.action.SENDTO""mailto:example@outlook.com")
    intent1.putExtra(
    "android.intent.extra.SUBJECT""This is the subject")
    intent1.putExtra(
    "android.intent.extra.TEXT""This is the message body")
    If SendAtt <> "" Then
        intent1.putExtra(
    "android.intent.extra.STREAM", SendAtt)
    End If
    intent1.WrapAsIntentChooser(
    "Send feedback")
    StartActivity(intent1)
    This worked using the old method but of course shows apps other than just email:

    Code:
    Dim EmailIntent As Email
    EmailIntent.To.Add (SendTo)
    EmailIntent.Body = SendBody
    EmailIntent.Subject = SendSub
    If SendAtt <> "" Then
        EmailIntent.Attachments.Add(SendAtt)
    End If

    StartActivity(EmailIntent.GetIntent)
     
    Last edited: Oct 11, 2014
  6. ivan.tellez

    ivan.tellez Active Member Licensed User

    You have to use a valid URI for the file, not just the path.

    Try this:
    Code:
    intent1.putExtra("android.intent.extra.STREAM""file://" + SendAtt)
     
  7. Kevin

    Kevin Well-Known Member Licensed User

    I tried it with the leading "file://" yesterday as well and either way it doesn't seem to attach a file. I thought perhaps it was some kind of folder/file access problem but I'm not so sure. For one, it is in a folder in "external root", but also it works with the old method anyway.

    It must be something simple, but I can't seem to figure it out.
     
  8. ivan.tellez

    ivan.tellez Active Member Licensed User

    Actually, It cant be done this way:

    • ACTION_SENDTO (for NO attachment)
    • ACTION_SEND (for one attachment)
    • ACTION_SEND_MULTIPLE (for multiple attachments)

    But, because the way B4A implements the intent Object, cant set the correct data in the Intent to select mail only apps with attachments.
     
  9. Kevin

    Kevin Well-Known Member Licensed User

    I wonder if it is possible to do with reflection or JO?
     
  10. Erel

    Erel Administrator Staff Member Licensed User

    The Email object from the Phone library implements both ACTION_SEND and ACTION_SEND_MULTIPLE intents.
     
  11. Kevin

    Kevin Well-Known Member Licensed User

    Hmmm. I can't try this right now but I wonder if this would allow attachments but also show only email apps in the list:


    Code:
    Dim EmailIntent As Email
    EmailIntent.To.Add (SendTo)
    EmailIntent.Body = SendBody
    EmailIntent.Subject = SendSub
    If SendAtt <> "" Then
      EmailIntent.Attachments.Add(SendAtt)
    End If

    Dim intent1 As Intent = EmailIntent.GetIntent
    intent1.WrapAsIntentChooser(
    "Send feedback")

    StartActivity(intent1)
     
  12. Erel

    Erel Administrator Staff Member Licensed User

    This will show all apps that registered to the ACTION_SEND action. If there is more than one attachment then ACTION_SEND_MULTIPLE will be used.
     
  13. Kevin

    Kevin Well-Known Member Licensed User

    In that case this will probably have the same result then. So perhaps it is just an issue in how Android (or its apps) utilize the intents?

    Is there a way to change the intent via reflection (or otherwise) to force it to only work with apps registered as email apps?
     
  14. Erel

    Erel Administrator Staff Member Licensed User

    If you find a Java code snippet that does it then I can help you implement it in B4A.
     
  15. Kevin

    Kevin Well-Known Member Licensed User

    It would appear that there is no built-in method of doing this regarding Android. However, the code below sounds promising but it looks like we'd have to put it in our own custom "chooser" list. I think it would be a decent solution for those of us who would like to be able to send attachments AND also only show actual email apps to the user.

    Lifted from: http://stackoverflow.com/questions/6506637/only-email-apps-to-resolve-an-intent (3rd answer down)


    Code:
    public static void sendEmail(final Context p_context, final String p_subject, final String p_body, final ArrayList<String> p_attachments)
    {
        
    try
        {
            
    PackageManager pm = p_context.getPackageManager();
            ResolveInfo selectedEmailActivity = 
    null;

            
    Intent emailDummyIntent = new Intent(Intent.ACTION_SENDTO);
            emailDummyIntent.setData(
    Uri.parse("mailto:some@emaildomain.com"));

            
    List<ResolveInfo> emailActivities = pm.queryIntentActivities(emailDummyIntent, 0);

            
    if (null == emailActivities || emailActivities.size() == 0)
            {
                
    Intent emailDummyIntentRFC822 = new Intent(Intent.ACTION_SEND_MULTIPLE);
                emailDummyIntentRFC822.setType(
    "message/rfc822");

                emailActivities = pm.queryIntentActivities(emailDummyIntentRFC822, 
    0);
            
    }

            if (null != emailActivities)
            {
                if (emailActivities.size() == 1)
                {
                    selectedEmailActivity = emailActivities.get(0);
                }
                else
                {
                    for (ResolveInfo currAvailableEmailActivity : emailActivities)
                    {
                        if (true == currAvailableEmailActivity.isDefault)
                        {
                            selectedEmailActivity = currAvailableEmailActivity;
                        }
                    }
                }

                if (null != selectedEmailActivity)
                {
                    // Send email using the only/default email activity
                    sendEmailUsingSelectedEmailApp(p_context, p_subject, p_body, p_attachments, selectedEmailActivity);
                }
                else
                {
                    final List<ResolveInfo> emailActivitiesForDialog = emailActivities;

                    String[] availableEmailAppsName = new String[emailActivitiesForDialog.size()];
                    for (int i = 0; i < emailActivitiesForDialog.size(); i++)
                    {
                        availableEmailAppsName[i] = emailActivitiesForDialog.get(i).activityInfo.applicationInfo.loadLabel(pm).toString();
                    }

                    AlertDialog.Builder builder = new AlertDialog.Builder(p_context);
                    builder.setTitle(R.string.select_mail_application_title);
                    builder.setItems(availableEmailAppsName, new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            sendEmailUsingSelectedEmailApp(p_context, p_subject, p_body, p_attachments, emailActivitiesForDialog.get(which));
                        }
                    });

                    builder.create().show();
                }
            }
            else
            {
                sendEmailUsingSelectedEmailApp(p_context, p_subject, p_body, p_attachments, null);
            }
        }
        catch (Exception ex)
        {
            Log.e(TAG, "Can't send email", ex);
        }
    }

    protected static void sendEmailUsingSelectedEmailApp(Context p_context, String p_subject, String p_body, ArrayList<String> p_attachments, ResolveInfo p_selectedEmailApp)
    {
        try
        {
            Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);

            String aEmailList[] = { "some@emaildomain.com"};

            emailIntent.putExtra(Intent.EXTRA_EMAIL, aEmailList);
            emailIntent.putExtra(Intent.EXTRA_SUBJECT, null != p_subject ? p_subject : "");
            emailIntent.putExtra(Intent.EXTRA_TEXT, null != p_body ? p_body : "");

            if (null != p_attachments && p_attachments.size() > 0)
            {
                ArrayList<Uri> attachmentsUris = new ArrayList<Uri>();

                // Convert from paths to Android friendly Parcelable Uri's
                for (String currAttachemntPath : p_attachments)
                {
                    File fileIn = new File(currAttachemntPath);
                    Uri currAttachemntUri = Uri.fromFile(fileIn);
                    attachmentsUris.add(currAttachemntUri);
                }
                emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachmentsUris);
            }

            if (null != p_selectedEmailApp)
            {
                Log.d(TAG, "Sending email using " + p_selectedEmailApp);
                emailIntent.setComponent(new ComponentName(p_selectedEmailApp.activityInfo.packageName, p_selectedEmailApp.activityInfo.name));

                p_context.startActivity(emailIntent);
            }
            else
            {
                Intent emailAppChooser = Intent.createChooser(emailIntent, "Select Email app");

                p_context.startActivity(emailAppChooser);
            }
        }
        catch (Exception ex)
        {
            Log.e(TAG, "Error sending email", ex);
        }
    }
     
  16. Kevin

    Kevin Well-Known Member Licensed User

    Success! I was able to get working B4A code from the Java example above. It isn't exactly the same but I believe it will provide the necessary functions. Only thing left to do would be to create a nice looking dialog for it complete with app icons. For now it just shows the results in an InputList dialog but I added the icon part in case anyone would want to work with this.

    If there is no attachment then it will use the method Ivan shared in this thread, complete with a native app chooser. If there is an attachment it will create a custom list shown in the InputList dialog.

    Tested with stock email app on Samsung Galaxy S5, GMail and AquaMail.

    Code:
    Sub SendEmail (SendTo As String, SendBody As String, SendSub As String, SendAtt As String)
        
    Dim FinalEmailIntent As Intent, sPackageName As String

        
    If SendAtt = "" Then ' Use new method - will not work with attachments
             FinalEmailIntent.Initialize("android.intent.action.SENDTO""mailto:" & SendTo)
            FinalEmailIntent.putExtra(
    "android.intent.extra.SUBJECT", SendSub)
             FinalEmailIntent.putExtra(
    "android.intent.extra.TEXT", SendBody)
            FinalEmailIntent.WrapAsIntentChooser(
    "Send E-mail")
                
    Else ' Make our own list of email apps
                    sPackageName = GetEmailPackage
                    
    If sPackageName = "/cancel/" Then Return
                    
    If sPackageName = "" Then
                        
    Msgbox ("Unable to send email.""")
                        
    Return
                    
    End If
                
                    
    Dim MyEmail As Email
                    MyEmail.To.Add (SendTo)
                  MyEmail.Body = SendBody
                     MyEmail.Subject = SendSub
                  MyEmail.Attachments.Add(SendAtt)
                    FinalEmailIntent = MyEmail.GetIntent
                    FinalEmailIntent.SetComponent (sPackageName)
        
    End If

        
    StartActivity (FinalEmailIntent)

    End Sub

    Sub GetEmailPackage As String
        
    Dim PM As PackageManager, DummyIntent As Intent, DummyIntent2 As Intent
        
    Dim EmailActivities As List, EmailAppNames As List, iPackage As Int, x As Int, tempString As String
        EmailActivities.Initialize: EmailAppNames.Initialize

        DummyIntent.Initialize(
    "android.intent.action.SENDTO""mailto:dummy@dummy.com")
        EmailActivities = PM.QueryIntentActivities (DummyIntent)

        
    If EmailActivities.Size = 0 Then
             DummyIntent2.Initialize(
    "android.intent.action.SENDMULTIPLE""")
            DummyIntent2.SetType (
    "message/rfc822")
            EmailActivities = PM.QueryIntentActivities (DummyIntent2)
        
    End If

        
    If EmailActivities.Size = 0 Then Return ""

        
    ' Get app labels & icons
        For x = 0 To EmailActivities.Size - 1
            
    Dim imgIcon As BitmapDrawable
            
    Dim sPackageName As String = EmailActivities.Get(x)
            sPackageName = sPackageName.SubString2 (
    0, sPackageName.IndexOf ("/"))
            tempString = PM.GetApplicationLabel(sPackageName)
            imgIcon = PM.GetApplicationIcon(sPackageName) 
    'App icon if desired
            EmailAppNames.Add (tempString)
        
    Next

        iPackage = 
    InputList (EmailAppNames,"Send email using",-1)
        
    If iPackage = DialogResponse.CANCEL Then Return "/cancel/"

        
    Return EmailActivities.Get (iPackage)

    End Sub
     
    Last edited: Oct 16, 2014
    Sannie72, cyiwin, DonManfred and 4 others like this.
  17. luke2012

    luke2012 Well-Known Member Licensed User

    Very interesting @Kevin ! So this could replace the code that use SMTP send method (protcol, user and password as parameters) ?
     
  18. Kevin

    Kevin Well-Known Member Licensed User

    If I understand you correctly then I would say that this would not replace sending email directly using SMTP. In that case you are directly submitting the email to the server, where as with this all you are doing is allowing the user to send an email using their favorite email app and that app is submitting the email to the server via SMTP.
     
    luke2012 and DonManfred like this.
  19. luke2012

    luke2012 Well-Known Member Licensed User

    If I understand you and the code correctly, it imply an user interaction (UI and not background process) each time it runs ?

    My target:
    I wish to replace the code within my app that ask info like user name and password to the user in order to parametrize the sender sub and introduce a code that use the current registered gmail account (without ask user credentials) to send emails in background mode when user tap on a specific button.
     
  20. Kevin

    Kevin Well-Known Member Licensed User

    I suppose the answer to what you are looking for depends on your purpose for sending these emails. But regarding the samples in this thread, yes it requires user interaction. Long story short, all it is doing and all it can do is present the user with a list of email apps on their phone with which to then send an email. I use it to allow users to send me an email that automatically includes the app name in the subject and the body is pre-populated with technical details about how the app is configured in order to help me understand their problem. It can also attach log and config files if necessary.

    I don't think that there is any way to send an email through the user's gmail account without their interaction at all. That would be a huge security risk.
     
Loading...