使用 Gmail api 通过 Android 应用发送邮件

Posted

技术标签:

【中文标题】使用 Gmail api 通过 Android 应用发送邮件【英文标题】:Use Gmail api for send mail via Android app 【发布时间】:2021-01-17 05:18:42 【问题描述】:

我知道有很多教程、问题等,过去几周我一直在努力,但无法弄清楚。

我正在开发一个 android 应用程序,我想使用我的 gmail 帐户向某个电子邮件地址发送电子邮件。

我已阅读所有 Google Developers 文档并尝试了所有示例,但仍然无法实现。

1.首先 - 我必须拥有 G Suite 帐户吗?它没有说我这样做,但Gmail Api overview 在 G Suite 部分下??

如果我不需要 G Suite -

2.这是right guide通过应用发送邮件吗?

如果是这样

3. 我怎样才能获得Gmail service?我在console.developers.google 中打开了一个项目并得到了OAuth 2.0 Client IDs json 文件,但是我找不到如何在Android 中使用它来获取凭据的解释。

拜托,我知道这些是个大问题,但我迫切需要帮助。请给我任何答案或正确的链接(我还没有访问过......)。我也将感谢通过聊天/邮件等提供的帮助。

谢谢

编辑 8/10 我现在的代码:

主要活动 调用 AsyncMail

AsyncMail.java

package com.send.mymailapplication;

import android.content.Context;
import android.content.res.AssetManager;
import android.os.AsyncTask;

import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.Base64;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.Message;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.Lists;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Properties;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;



public class AsyncMail extends AsyncTask<String, Void, Void> 

    Context context;

    static HttpTransport HTTP_TRANSPORT = AndroidHttp.newCompatibleTransport();
    static JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

    public AsyncMail (Context c) 
        context = c;
    


    @Override
    protected Void doInBackground (String... strings) 


        try 

            AssetManager assetManager = context.getAssets();
            InputStream jsonStream = assetManager.open("mail.json");

            // those 2 tryings also works but gave me the same error
            /*ServiceAccountCredentials sourceCredentials = ServiceAccountCredentials.fromStream(jsonStream);
            sourceCredentials = (ServiceAccountCredentials) sourceCredentials.createScoped(Arrays.asList("https://mail.google.com/", "https://www.googleapis.com/auth/iam"));

            GoogleCredential credential = GoogleCredential.fromStream(jsonStream).createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform", GmailScopes.MAIL_GOOGLE_COM));
            Gmail gmail = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).build();*/


            GoogleCredentials credential = GoogleCredentials.fromStream(jsonStream)
                           .createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/dialogflow", GmailScopes.MAIL_GOOGLE_COM));
            credential.refreshIfExpired();
            AccessToken accessToken = credential.getAccessToken ();
            String AuthToken = accessToken.getTokenValue ();


            HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter (credential);


            Gmail gmail = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer).build();

            MimeMessage mimeMessage = createEmail ("avitalh@gmail.com", "sendmail@mail-291708.iam.gserviceaccount.com", "subbb", "bodyyyy");

            Message message = createMessageWithEmail(mimeMessage);

            gmail.users().messages().send ("sendmail@mail-291708.iam.gserviceaccount.com", message).execute();


         catch (IOException | MessagingException e) 
            e.printStackTrace ();
        
        
        return null;
    

    private Message createMessageWithEmail (MimeMessage emailContent) throws IOException, MessagingException 

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        emailContent.writeTo(buffer);
        byte[] bytes = buffer.toByteArray();
        String encodedEmail = Base64.encodeBase64URLSafeString(bytes);
        Message message = new Message ();
        message.setRaw(encodedEmail);
        return message;
    

    public static MimeMessage createEmail(String to, String from, String subject, String bodyText) throws MessagingException 

        Properties props = new Properties ();
        Session session = Session.getDefaultInstance(props, null);

        MimeMessage email = new MimeMessage(session);

        email.setFrom(new InternetAddress (from));
        email.addRecipient(javax.mail.Message.RecipientType.TO,
                new InternetAddress(to));
        email.setSubject(subject);
        email.setText(bodyText);
        return email;
    


我也不确定:

    我应该获得哪些凭据?我认为它是ServiceAccountCredentials,但documentation 说要使用GoogleCredential,它已被弃用,所以我尝试了GoogleCredentials

    我应该使用哪个电子邮件地址作为发件人 - gmail?服务帐户? “我”?有两个地方我应该使用它

    我应该如何处理令牌?

    在 Google Api 控制台顶部显示“您的免费试用正在等待:立即激活...” - 我必须激活帐户才能使用 api?我不这么认为,但不确定

【问题讨论】:

你知道答案吗:***.com/questions/68738316/… 对不起@user3505444,我不知道。我放弃了使用 Gmail 发送邮件...我使用了另一个 3rd 方 这太疯狂了,过去几年的文档看起来像大 0。谷歌正朝着错误的方向前进。等我弄清楚了,我会发布回复。 @user3505444 是的,这太疯狂了。如果我们不能指望谷歌,还剩下什么?...祝你好运!!! 【参考方案1】:

我会尽量回答你的多个问题

    ,您不需要 G Suite 帐户即可使用 Gmail API,您可以使用普通 gmail 帐户。

    您链接的指南很好,只是认为在应用程序或控制台中,API 使用 REST 架构处理 HTTPs 请求,因此您可以在任何支持 HTTP 请求的平台中使用 API。

    最后一点很大程度上取决于您使用的架构,并考虑到我不是 Android 专家。但是“困难”的部分将是获得用户的授权,但我相信你可以正常使用Java。

有用的链接

G Suite for Android Google Services sample Authenticate OAuth2 in Android JAVA Gmail API quicsktart

【讨论】:

非常感谢您的回答!关于答案 3,正如我所写,我想使用 my 帐户发送邮件,所以我不需要用户许可,对吧?我只是找不到如何获取凭据。你真的帮了我,谢谢! 无论哪种方式,您都需要使用凭据。你在什么时候卡住了? 我一直在为 public static Message sendMessage 方法获取授权的 Gmail API 实例。所有示例都用于选择使用帐户对话框,当我想使用我自己的时。另外,我不确定-您说我需要使用 HTTPS 请求(我可以使用 Volley 来完成),但看起来 Gmail api 不需要它,不是吗?我真的迷路了......这是一个如此基本的事情,我找不到任何完整的解释。非常感谢您的患者! 帐户对话框?考虑到该人(在这种情况下是您)将需要从谷歌平台提供访问权限才能访问您的数据。我强烈建议您阅读有关OAuth2 in native Apps 和OAuth in Java 的文章 我阅读了很多...我有一个基本问题:我是否必须赢得一个域才能使用 Gmail api 从 my 帐户发送电子邮件? G Suite 帐户或同意屏幕的域?我想不通!【参考方案2】:

初始步骤:

    您需要使用您的 Google 帐户登录网络浏览器。

    如果不这样做,您将收到重定向错误错误 400 等。

    将应用添加到谷歌云平台, 为您将使用的所有签名添加 sha-1 密钥

    创建一个带有讨厌的重定向 URL 的网络应用程序!

最终的控制台将如下所示:

    下载 网络应用 credential.json。将其放入新文件夹 app/resources

    Gmail 类快速入门

    private static final String APPLICATION_NAME = "com.emable.getlabels" ; 
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    private static final String TOKENS_DIRECTORY_PATH = "tokens";
    
    /**
     * Global instance of the scopes required by this quickstart.
     * If modifying these scopes, delete your previously saved tokens/ folder.
     */
    
    private static final List<String> SCOPES = Collections.singletonList( GmailScopes.GMAIL_LABELS ) ;
    private static final String CREDENTIALS_FILE_PATH = "/credentials.json";
    
    
    
    public static Message sendMessage(Gmail service,
                                      String userId,
                                      MimeMessage emailContent)
            throws MessagingException, IOException 
        Message message = createMessageWithEmail(emailContent);
        message = service.users().messages().send(userId, message).execute();
    
        System.out.println("Message id: " + message.getId());
        System.out.println(message.toPrettyString());
        return message;
    
    
     public static Message createMessageWithEmail(MimeMessage emailContent)
             throws MessagingException, IOException 
         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
         emailContent.writeTo(buffer);
         byte[] bytes = buffer.toByteArray();
         String encodedEmail = Base64.encodeBase64URLSafeString(bytes);
         Message message = new Message();
         message.setRaw(encodedEmail);
         return message;
     
    
    private static LocalServerReceiver localServerReceiver = null;
    private static AuthorizationCodeInstalledApp localAuthorizationCodeInstalledApp = null;
    
    private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT,Context context, String personId) throws IOException 
        // Load client secrets.
        InputStream in = GmailQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
        if (in == null) 
            throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
        
    
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
    
        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
                .setDataStoreFactory(new FileDataStoreFactory(  context.getFilesDir() )) // new java.io.File(TOKENS_DIRECTORY_PATH)))
                .setAccessType("online")
                .setApprovalPrompt("force")
                .build();
         if (localServerReceiver == null) 
             localServerReceiver = new LocalServerReceiver.Builder().setPort(8888).build();
         else
             localServerReceiver.stop();
             localServerReceiver = new LocalServerReceiver.Builder().setPort(8888).build();
         
    
        AuthorizationCodeInstalledApp  lAuthorizationCodeInstalledApp = new AuthorizationCodeInstalledApp(flow, localServerReceiver) 
                protected void onAuthorization(AuthorizationCodeRequestUrl authorizationUrl) throws IOException 
                    String url = (authorizationUrl.build());
                    Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                    (context).startActivity(browserIntent);
                
            ;
    
        return lAuthorizationCodeInstalledApp.authorize("user");
    
    
    public static void start(Context c,String personId, TextView tview) throws IOException, GeneralSecurityException 
        // Build a new authorized API client service.
    
        final NetHttpTransport HTTP_TRANSPORT = new com.google.api.client.http.javanet.NetHttpTransport();
    
        Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY,  getCredentials(HTTP_TRANSPORT, c, personId))
                .setApplicationName(APPLICATION_NAME)
                .build();
        new Handler(Looper.getMainLooper()).post(new Runnable() 
            @Override
            public void run() 
                Toast toast = Toast.makeText(c,"Gmail service ok", Toast.LENGTH_SHORT);
                toast.show();
            
        );
        Log.i("TAG", "BLA");
        // Print the labels in the user's account.
        String userid = "me";
        ListLabelsResponse listResponse = service.users().labels().list(userid).execute();
        List<Label> labels = listResponse.getLabels();
        //service.
        if (labels.isEmpty()) 
            Log.i("TAG","  No Labels ");
    
         else 
    
            for (Label label : labels) 
                new Handler(Looper.getMainLooper()).post(new Runnable() 
                    @Override
                    public void run() 
                        Toast toast = Toast.makeText(c, label.toString(), Toast.LENGTH_SHORT);
                        toast.show();
                    
                );
            
        
    
    

这个函数可以通过例如 Avtivity 中的按钮调用。

   public void SendEmailTest(Context context)

       AsyncTask<Void, Void, String> asyncTask = new AsyncTask<Void, Void, String>() 
           @Override
           protected String doInBackground(Void... params) 
               try 
                   try 
                      // ignore personId, tview your dont  really need them
                       GmailQuickstart.start(context, personId, tview);
                    catch (IOException e) 
                       e.printStackTrace();
                    catch (GeneralSecurityException e) 
                       e.printStackTrace();
                   
                catch (Exception e) 
                   e.printStackTrace();
               
               return "";

           ;

       ;
       asyncTask.execute();





   

添加用户权限:

<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SMS_RECEIVED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.android.vending.BILLING" />

分级:

plugins 
    id 'com.android.application'


android 
    compileSdkVersion 30
    buildToolsVersion "30.0.3"
    repositories 
        google()
    
    defaultConfig 
        applicationId "      bla bla bla bla bla your app id here"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 2
        versionName "1.0"
        multiDexEnabled true


        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    

    buildTypes 
        release 
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            debuggable false
        
    
    compileOptions 
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    
    buildFeatures 
        viewBinding true
    
    lintOptions 
        checkReleaseBuilds false
    






dependencies 

    implementation 'androidx.multidex:multidex:2.0.1'



    def billing_version = "4.0.0"

    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'com.google.android.gms:play-services-auth:19.2.0'
    implementation 'androidx.navigation:navigation-fragment:2.3.0'
    implementation 'androidx.navigation:navigation-ui:2.3.0'
    debugImplementation files('app/libs')
    debugImplementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
    implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation "com.android.billingclient:billing:$billing_version"

    implementation 'com.google.api-client:google-api-client:1.23.0'
    implementation 'com.google.oauth-client:google-oauth-client-jetty:1.23.0'
    implementation 'com.google.apis:google-api-services-gmail:v1-rev83-1.23.0'





将 GmailScopes.GMAIL_LABELS 更改为 what ever 上帝帮助我们所有人

【讨论】:

对你有好处!!我已经接受了 aup 并使用第 3 方发送电子邮件...我认为“上帝帮助我们所有人”是一个非常好的总结... :)

以上是关于使用 Gmail api 通过 Android 应用发送邮件的主要内容,如果未能解决你的问题,请参考以下文章

将 Android GoogleSignIn 与 GmailScopes.GMAIL_SEND 一起使用(gmail api)

如何使用谷歌联系人api以编程方式将手机联系人同步到android中的gmail

如何在客户端中使用 Firebase Auth gmail 保护 NestJS 中的 API?

如何从我的 Android 应用程序访问新的 Gmail API?

使用 Gmail API 发送电子邮件,在正文中编码希腊字符

GMAIL API 历史端点仅在 Android 上返回 404 not found 响应代码