Android ACTION_SEND意图未填充主题或正文

3
我在我的应用程序中编写了一段代码,允许用户向开发人员发送电子邮件。它应该预填写收件人字段、主题字段和正文字段。然而,当我运行时,它只填充了收件人,而忽略了其他额外信息,如主题、正文和选择器文本。我在两个测试设备上看到了这种行为:一个运行Lollipop(Verizon三星Galaxy Note 4),另一个运行Jelly Bean 4.2.2(Samsung Fascinate on CM10.1),尽管我不知道这是否与问题有关。
private void sendHelpEmail() {
    Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));
    // prompts email clients only
    email.setType("message/rfc822");

    email.putExtra(Intent.EXTRA_EMAIL, new String[] {getString(R.string.about_email)});
    email.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.login_help_subject));
    email.putExtra(Intent.EXTRA_TEXT, getString(R.string.login_help_body, classButton.text(), Txt_Student.getText().toString()));

    try {
        // the user can choose the email client
        startActivity(Intent.createChooser(email, getString(R.string.login_help_chooser)));
        } catch (android.content.ActivityNotFoundException ex) {
            Toast.makeText(mThis, R.string.login_help_error, Toast.LENGTH_LONG).show();
        }
}

当填写了收件人电子邮件地址时,为什么主题和正文会被忽略?

1
我想知道是否有一些电子邮件客户端应用程序会忽略接收到的意图附加信息。 - Stealth Rabbi
它甚至在选择应用程序之前忽略了选择器文本。然后我要选择的应用程序是Gmail,因此似乎应该有一些意图至少与Gmail配合使用。 - Scott
当我为发送电子邮件创建Intent时,我使用了一个参数构造函数来创建Intent(只使用操作)。您不需要使用mailto:属性。此外,您不必调用setType()方法。 - Stealth Rabbi
同样的代码对我有效。我建议您验证您的字符串资源是否正确(包括在当前语言/风格等中未被覆盖)。 - Haspemulator
奇怪。你在用什么?我即使将资源替换为硬编码的字符串,结果还是一样的。 - Scott
Galaxy Note 4,棒棒糖(5.0.1)。适用于Gmail、Outlook和内置电子邮件客户端。请在找到解决方案后发布您的答案。 - Haspemulator
6个回答

7
以下代码对我有效(刚刚尝试过):
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("message/rfc822");
i.putExtra(Intent.EXTRA_EMAIL, new String[]{"foo@bar.com"});
i.putExtra(Intent.EXTRA_SUBJECT, "subject of email");
i.putExtra(Intent.EXTRA_TEXT   , "body of email");
try {
  startActivity(Intent.createChooser(i, "Choose email...");
} catch (android.content.ActivityNotFoundException ex) {
    // handle edge case where no email client is installed
}

3

Intent intent = new Intent(INTENT.ACTION_SENDTO); 只会启动电子邮件客户端,但是在某些设备上不会填充SUBJECTBODY字段。(我认为这些是边缘情况)。

Intent intent = new Intent(INTENT.ACTION_SEND); 会启动所有可以发送多部分消息的应用程序,例如:WhatsApp、Telegram、Gmail等,在所有设备上始终填充SUBJECTBODY字段。


谢谢,使用ACTION_SEND而不是ACTION_SENDTO解决了缺少主题的错误,但是除电子邮件应用程序之外还会出现其他应用程序,这一点非常令人恼火!我可以理解WhatsApp和Telegram,但为什么像Google Maps这样的应用程序也在其中?-令人难以置信的是,在您的答案两年半之后,Google仍然没有修复Android API中的此大错误。 (“某些设备”不是Google的借口,因为我的客户不愿意接受我) - Fry Simpson

3

有几种方法可以克服这个问题。非常简单的解释是,在Api 29和Api 30中,Android系统的一些细微变化影响了接收传入数据的方式,简而言之,Uri.parse("mailto:") 以及 Intent Extras 部分正在“碰撞”(做着相同的事情),接收应用程序不确定从哪里获取数据。

因为已经到了2023年,我永远不会建议工程师编写新的Android代码使用Java,所以请使用Kotlin回答:

方法1 - 在Uri.parse内提供所有信息

val intent = Intent(Intent.ACTION_SENDTO)
val subject = "Some email subject"
intent.data = Uri.parse("mailto:firstaddress@email.com,secondaddress@email.com?subject=$subject")
startActivity(intent)

结果

方法2 - 使用自己的意图“选择器”

由于有许多不同的应用程序能够处理各种意图,因此您可以在意图中指定一个单独的意图作为“选择器”,这意味着您可以定义所提供的应用程序的行为。这意味着您可以纯粹地在意图附加项中构建电子邮件数据,但使用“mailto”方法定义选择器表。示例代码:

val selectorIntent = Intent(Intent.ACTION_SENDTO)
selectorIntent.data = Uri.parse("mailto:") // this ensures only email apps offered
 
val emailIntent = Intent(Intent.ACTION_SEND)
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf("address@mail.com", "anotheraddress@mail.com"))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "The subject")
emailIntent.putExtra(Intent.EXTRA_TEXT, "The email body")
emailIntent.selector = selectorIntent
 
startActivity(Intent.createChooser(emailIntent, "Send email..."))

2

尝试这种方法,对我很有效。

private void sendMail() {

    Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "xx@xx.com", null));
    emailIntent.putExtra(Intent.EXTRA_SUBJECT,getResources().getString(R.string.mail_txt));
    startActivity(Intent.createChooser(emailIntent, "Send email..."));

}         

1
不,这真的很奇怪 - 这个代码在其他人那里似乎可以运行,但在我的两个测试设备上却无法运行。 - Scott

0
我已经根据以下内容修改了这个gist,这个工具类可以根据任何人的需要进行修改,并且它将对电子邮件操作非常有帮助。
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Patterns;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.File;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;


public final class IntentEmail {
    private final Context context;
    private final Set<String> to = new LinkedHashSet<>();
    private final Set<String> cc = new LinkedHashSet<>();
    private final Set<String> bcc = new LinkedHashSet<>();
    private String subject;
    private String body;
    private File attachment;
    private String intentChooser;

    private IntentEmail(@NonNull Context context) {
        this.context = checkNotNull(context);
    }


    public static IntentEmail from(@NonNull Context context) {
        return new IntentEmail(context);
    }

    private static <T> T checkNotNull(T object) {
        if (object == null) {
            throw new IllegalArgumentException("Argument must not be null");
        }
        return object;
    }

    static String encodeRecipient(String recipient) {
        int index = recipient.lastIndexOf('@');
        String localPart = recipient.substring(0, index);
        String host = recipient.substring(index + 1);
        return Uri.encode(localPart) + "@" + Uri.encode(host);
    }

    static String fixLineBreaks(String text) {
        return text.replaceAll("\r\n", "\n").replace('\r', '\n').replaceAll("\n", "\r\n");
    }

    public IntentEmail setIntentChooser(@Nullable String intentChooser) {
        this.intentChooser = intentChooser;
        return this;
    }

    public IntentEmail attachment(@Nullable File attachment) {
        this.attachment = attachment;
        return this;
    }

    public IntentEmail to(@NonNull String to) {
        checkEmail(to);
        this.to.add(to);
        return this;
    }

    public IntentEmail to(@NonNull Collection<String> to) {
        checkNotNull(to);
        for (String email : to) {
            checkEmail(email);
        }
        this.to.addAll(to);
        return this;
    }

    public IntentEmail cc(@NonNull String cc) {
        checkEmail(cc);
        this.cc.add(cc);
        return this;
    }

    public IntentEmail cc(@NonNull Collection<String> cc) {
        checkNotNull(cc);
        for (String email : cc) {
            checkEmail(email);
        }
        this.cc.addAll(cc);
        return this;
    }

    public IntentEmail bcc(@NonNull String bcc) {
        checkEmail(bcc);
        this.bcc.add(bcc);
        return this;
    }

    public IntentEmail bcc(@NonNull Collection<String> bcc) {
        checkNotNull(bcc);
        for (String email : bcc) {
            checkEmail(email);
        }
        this.bcc.addAll(bcc);
        return this;
    }

    public IntentEmail subject(@NonNull String subject) {
        checkNotNull(subject);
        checkNoLineBreaks(subject);
        this.subject = subject;
        return this;
    }

    public IntentEmail body(@NonNull String body) {
        checkNotNull(body);
        this.body = fixLineBreaks(body);
        return this;
    }

    public void startActivity() {
        Intent intent = build();
        if (!(context instanceof Activity)) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        if(intentChooser != null){
            context.startActivity(Intent.createChooser(intent, "Send Email"));
        }else {
            context.startActivity(intent);
        }
    }

    public Intent build() {
        Uri mailtoUri = constructMailtoUri();
        Intent intent = new Intent(Intent.ACTION_SENDTO, mailtoUri);
        if(attachment != null){
            intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(attachment));
        }
        return intent;
    }

    private Uri constructMailtoUri() {
        StringBuilder mailto = new StringBuilder(1024);
        mailto.append("mailto:");
        addRecipients(mailto, to);

        boolean hasQueryParameters;
        hasQueryParameters = addRecipientQueryParameters(mailto, "cc", cc, false);
        hasQueryParameters = addRecipientQueryParameters(mailto, "bcc", bcc, hasQueryParameters);
        hasQueryParameters = addQueryParameter(mailto, "subject", subject, hasQueryParameters);
        addQueryParameter(mailto, "body", body, hasQueryParameters);

        return Uri.parse(mailto.toString());
    }

    private boolean addQueryParameter(StringBuilder mailto, String field, String value, boolean hasQueryParameters) {
        if (value == null) {
            return hasQueryParameters;
        }
        mailto.append(hasQueryParameters ? '&' : '?').append(field).append('=').append(Uri.encode(value));
        return true;
    }

    private boolean addRecipientQueryParameters(StringBuilder mailto, String field, Set<String> recipients, boolean hasQueryParameters) {
        if (recipients.isEmpty()) {
            return hasQueryParameters;
        }
        mailto.append(hasQueryParameters ? '&' : '?').append(field).append('=');
        addRecipients(mailto, recipients);
        return true;
    }

    private void addRecipients(StringBuilder mailto, Set<String> recipients) {
        if (recipients.isEmpty()) {
            return;
        }
        for (String recipient : recipients) {
            mailto.append(encodeRecipient(recipient));
            mailto.append(',');
        }
        mailto.setLength(mailto.length() - 1);
    }

    private void checkEmail(String email) {
        checkNotNull(email);
        if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
            throw new IllegalArgumentException("Argument is not a valid email address (according to " +
                    "Patterns.EMAIL_ADDRESS)");
        }
    }

    private void checkNoLineBreaks(String text) {
        boolean containsCarriageReturn = text.indexOf('\r') != -1;
        boolean containsLineFeed = text.indexOf('\n') != -1;
        if (containsCarriageReturn || containsLineFeed) {
            throw new IllegalArgumentException("Argument must not contain line breaks");
        }
    }
}

如何使用:

try {
    IntentEmail.from(this)
        .to("mailId@mail.com")
        .subject(getString(R.string.app_name))
        .body("Body Text")
        .attachment(file)
        .setIntentChooser("Send Email")
        .startActivity();
} catch (Exception e) {
        Logger.e(e);
}

-1

我刚遇到了这个问题,以下是我是如何解决的:

检查一下电子邮件客户端(在我的情况下是 Gmail)是否只是重复使用未发送的草稿而不是新建一封电子邮件。因为当这样做时,客户端似乎会忽略 Intent.EXTRA_SUBJECTIntent.EXTRA_TEXT


什么是“未发送的草稿”?这并不是一个很好的“解决方案”。看起来可能是一些有关调试的好建议,但由于模棱两可的术语“未发送的草稿”,很难知道这里的意图是什么。 - Carl Rossman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接