package com.ghostflying.portalwaitinglist.util; import com.ghostflying.portalwaitinglist.dao.datahelper.PortalEventHelper; import com.ghostflying.portalwaitinglist.model.Message; import com.ghostflying.portalwaitinglist.model.MessageList; import com.google.gson.Gson; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.MultipartBuilder; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import retrofit.RequestInterceptor; import retrofit.RestAdapter; /** * Created by ghostflying on 11/20/14. * <br> * An util class for query and process data. */ public class GMailServiceUtil { private static final long ONE_DAY_MILLISECONDS = 3600L * 24 * 1000; private static final String QUERY_STRING = "from:(super-ops@google.com OR ingress-support@google.com) subject:(Ingress Portal)"; private static final String QUERY_MESSAGE_FORMAT = "?format=full"; private static final String QUERY_MESSAGE_METADATA_DATE = "&metadataHeaders=date"; private static final String QUERY_MESSAGE_METADATA_SUBJECT = "&metadataHeaders=subject"; private static final String DEFAULT_AFTER_STR = "1995/07/08"; private static final String PART_REQUEST_BASE_PATH = "GET /gmail/v1/users/me/messages/"; private static final String BATCH_REQUEST_URL = "https://www.googleapis.com/batch"; private static final MediaType HTTP = MediaType.parse("application/http"); private static GMailServiceUtil instance; private Gson gson; private GMailService gmailService; private String token; /** * Private constructor. * @param token the token to query. */ private GMailServiceUtil(final String token){ RequestInterceptor requestInterceptor = new RequestInterceptor() { @Override public void intercept(RequestFacade request) { request.addHeader("Authorization", "Bearer " + token); } }; RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://www.googleapis.com/gmail/v1") .setRequestInterceptor(requestInterceptor) .build(); gmailService = restAdapter.create(GMailService.class); this.token = token; } /** * Get an instance of GMailServiceUtil. * @return the instance. */ public static GMailServiceUtil getInstance(final String token, boolean forceRenew){ if (instance == null || forceRenew){ instance = new GMailServiceUtil(token); } return instance; } /** * Get all messages related to portal submit. * @param helper the helper for portal event. * @return list of Message */ public ArrayList<Message> getPortalMessages(PortalEventHelper helper) throws IOException{ ArrayList<Message> messages = new ArrayList<>(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); //minus a day to avoid the effect of time zone or other problem. String afterStr; long timestamp = helper.getLastEventTimestamp(); if (timestamp != 0){ afterStr = dateFormat.format(new Date(timestamp - ONE_DAY_MILLISECONDS)); } else { afterStr = DEFAULT_AFTER_STR; } ArrayList<MessageList.MessageId> messageIds = getAllPortalMessageIds("after:" + afterStr); // remove all exist messageId removeDuplicate(messageIds, helper); // Query and add Message to list. if (messageIds.size() > 0){ int page = (messageIds.size() - 1) / 100 + 1; // reverse it to get a list in asc order by date. for (int i = page; i > 0; i--){ int count = i == page ? messageIds.size() - 100 * (i -1) : 100; messages.addAll(getBatchMessages(messageIds, (i - 1) * 100, count)); } } return messages; } /** * Get messages by batch request * @param messageIds the id list. * @param start start index. * @param count count. * @return the parsed messages. * @throws IOException the response has error. */ private ArrayList<Message> getBatchMessages(ArrayList<MessageList.MessageId> messageIds, int start, int count) throws IOException{ OkHttpClient client = new OkHttpClient(); ArrayList<Message> messages = new ArrayList<>(); MultipartBuilder builder = new MultipartBuilder(); builder.type(MultipartBuilder.MIXED); // reverse the id to get a list i asc order by date. for (int i = start + count - 1; i > start - 1; i--){ byte[] request = (PART_REQUEST_BASE_PATH + messageIds.get(i).getId() + QUERY_MESSAGE_FORMAT + QUERY_MESSAGE_METADATA_DATE + QUERY_MESSAGE_METADATA_SUBJECT + "\n").getBytes(); RequestBody partRequest = RequestBody.create(HTTP, request); builder.addPart(partRequest); } Request request = new Request.Builder() .url(BATCH_REQUEST_URL) .post(builder.build()) .header("Authorization", "Bearer " + token) .build(); Response response = client.newCall(request).execute(); String contentType = response.header("Content-Type"); String responseBody = response.body().string(); if (RegexUtil.getInstance().isFound(RegexUtil.FIND_BOUNDARY, contentType)){ String boundary = RegexUtil.getInstance().getMatchedStr(); String[] eachResponses = responseBody.split("--" + boundary); for (int i = 1; i < count + 1; i++){ messages.add(parseEachInBatch(eachResponses[i])); } return messages; } else throw new IOException("Can not found the boundary, maybe request failed."); } private Message parseEachInBatch(String eachStr) throws IOException{ // Initial the gson and pattern. if (gson == null){ gson = new Gson(); } if (RegexUtil.getInstance().isFound(RegexUtil.EACH_JSON_IN_BATCH, eachStr)) return gson.fromJson(RegexUtil.getInstance().getMatchedStr(), Message.class); else throw new IOException("Parse response " + eachStr + " error, maybe the fetch failed."); } /** * Get all messageIds related portal submit. * @param afterStr the string about date to reduce the query response. * @return the list of messageId needed. */ private ArrayList<MessageList.MessageId> getAllPortalMessageIds(String afterStr){ ArrayList<MessageList.MessageId> messageIds = new ArrayList<>(); MessageList messageList; String pageToken = null; do{ messageList = gmailService.getMessages(QUERY_STRING + afterStr, pageToken); messageIds.addAll(messageList.getMessages()); pageToken = messageList.getNextPageToken(); }while (messageList.hasNextPage()); return messageIds; } /** * Remove the fetched messageId * @param ids the messageId list. * @param helper the database helper to query. */ private void removeDuplicate(ArrayList<MessageList.MessageId> ids, PortalEventHelper helper){ for (MessageList.MessageId id : ids.toArray(new MessageList.MessageId[ids.size()])){ if (helper.isExist(id.getId())) ids.remove(id); } } }