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

Use Gmail api for send mail via Android app

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

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

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

1.首先 - 我必须拥有一个 G Suite 帐户吗?它并没有说我知道,但是 Gmail Api overview 在 G Suite 部分下??

如果我不需要 G Suite -

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

如果是

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

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

谢谢

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

主要Activity 调用 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;
    }
}

我也不确定:

  1. 我应该获得哪些证书?我会认为它是 ServiceAccountCredentialsdocumentation 说使用 GoogleCredential 已弃用所以我尝试了 GoogleCredentials

  2. 我应该使用哪个电子邮件地址作为发件人 - gmail?服务帐号? “我”?我应该在 2 个地方使用它

  3. 我应该如何使用令牌?

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

我会尽力回答你的多重问题

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

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

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

    正常完成

有用的链接

初始步骤:

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

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

  2. 将应用添加到google云平台, 添加 sha-1 密钥,对于所有签名,您将使用

  3. 创建一个讨厌重定向的 Web 应用程序 URL !!

最终控制台将如下所示:

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

    class 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" />

Gradle:

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 上帝帮助我们所有人