1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email.provider;
18
19import android.content.ContentUris;
20import android.content.ContentValues;
21import android.content.Context;
22import android.database.Cursor;
23import android.net.Uri;
24
25import com.android.email.LegacyConversions;
26import com.android.emailcommon.Logging;
27import com.android.emailcommon.internet.MimeUtility;
28import com.android.emailcommon.mail.Message;
29import com.android.emailcommon.mail.MessagingException;
30import com.android.emailcommon.mail.Part;
31import com.android.emailcommon.provider.Account;
32import com.android.emailcommon.provider.EmailContent;
33import com.android.emailcommon.provider.EmailContent.Attachment;
34import com.android.emailcommon.provider.EmailContent.MessageColumns;
35import com.android.emailcommon.provider.EmailContent.SyncColumns;
36import com.android.emailcommon.provider.Mailbox;
37import com.android.emailcommon.utility.ConversionUtilities;
38import com.android.mail.utils.LogUtils;
39
40import java.io.IOException;
41import java.util.ArrayList;
42
43public class Utilities {
44    /**
45     * Copy one downloaded message (which may have partially-loaded sections)
46     * into a newly created EmailProvider Message, given the account and mailbox
47     *
48     * @param message the remote message we've just downloaded
49     * @param account the account it will be stored into
50     * @param folder the mailbox it will be stored into
51     * @param loadStatus when complete, the message will be marked with this status (e.g.
52     *        EmailContent.Message.LOADED)
53     */
54    public static void copyOneMessageToProvider(Context context, Message message, Account account,
55            Mailbox folder, int loadStatus) {
56        EmailContent.Message localMessage = null;
57        Cursor c = null;
58        try {
59            c = context.getContentResolver().query(
60                    EmailContent.Message.CONTENT_URI,
61                    EmailContent.Message.CONTENT_PROJECTION,
62                    EmailContent.MessageColumns.ACCOUNT_KEY + "=?" +
63                            " AND " + MessageColumns.MAILBOX_KEY + "=?" +
64                            " AND " + SyncColumns.SERVER_ID + "=?",
65                            new String[] {
66                            String.valueOf(account.mId),
67                            String.valueOf(folder.mId),
68                            String.valueOf(message.getUid())
69                    },
70                    null);
71            if (c == null) {
72                return;
73            } else if (c.moveToNext()) {
74                localMessage = EmailContent.getContent(c, EmailContent.Message.class);
75            } else {
76                localMessage = new EmailContent.Message();
77            }
78            localMessage.mMailboxKey = folder.mId;
79            localMessage.mAccountKey = account.mId;
80            copyOneMessageToProvider(context, message, localMessage, loadStatus);
81        } finally {
82            if (c != null) {
83                c.close();
84            }
85        }
86    }
87
88    /**
89     * Copy one downloaded message (which may have partially-loaded sections)
90     * into an already-created EmailProvider Message
91     *
92     * @param message the remote message we've just downloaded
93     * @param localMessage the EmailProvider Message, already created
94     * @param loadStatus when complete, the message will be marked with this status (e.g.
95     *        EmailContent.Message.LOADED)
96     * @param context the context to be used for EmailProvider
97     */
98    public static void copyOneMessageToProvider(Context context, Message message,
99            EmailContent.Message localMessage, int loadStatus) {
100        try {
101            EmailContent.Body body = null;
102            if (localMessage.mId != EmailContent.Message.NO_MESSAGE) {
103                body = EmailContent.Body.restoreBodyWithMessageId(context, localMessage.mId);
104            }
105            if (body == null) {
106                body = new EmailContent.Body();
107            }
108            try {
109                // Copy the fields that are available into the message object
110                LegacyConversions.updateMessageFields(localMessage, message,
111                        localMessage.mAccountKey, localMessage.mMailboxKey);
112
113                // Now process body parts & attachments
114                ArrayList<Part> viewables = new ArrayList<Part>();
115                ArrayList<Part> attachments = new ArrayList<Part>();
116                MimeUtility.collectParts(message, viewables, attachments);
117
118                final ConversionUtilities.BodyFieldData data =
119                        ConversionUtilities.parseBodyFields(viewables);
120
121                // set body and local message values
122                localMessage.setFlags(data.isQuotedReply, data.isQuotedForward);
123                localMessage.mSnippet = data.snippet;
124                body.mTextContent = data.textContent;
125                body.mHtmlContent = data.htmlContent;
126                body.mHtmlReply = data.htmlReply;
127                body.mTextReply = data.textReply;
128                body.mIntroText = data.introText;
129
130                // Commit the message & body to the local store immediately
131                saveOrUpdate(localMessage, context);
132                body.mMessageKey = localMessage.mId;
133                saveOrUpdate(body, context);
134
135                // process (and save) attachments
136                if (loadStatus != EmailContent.Message.FLAG_LOADED_PARTIAL
137                        && loadStatus != EmailContent.Message.FLAG_LOADED_UNKNOWN) {
138                    // TODO(pwestbro): What should happen with unknown status?
139                    LegacyConversions.updateAttachments(context, localMessage, attachments);
140                } else {
141                    EmailContent.Attachment att = new EmailContent.Attachment();
142                    // Since we haven't actually loaded the attachment, we're just putting
143                    // a dummy placeholder here. When the user taps on it, we'll load the attachment
144                    // for real.
145                    // TODO: This is not a great way to model this. What we're saying is, we don't
146                    // have the complete message, without paying any attention to what we do have.
147                    // Did the main body exceed the maximum initial size? If so, we really might
148                    // not have any attachments at all, and we just need a button somewhere that
149                    // says "load the rest of the message".
150                    // Or, what if we were able to load some, but not all of the attachments?
151                    // Then we should ideally not be dropping the data we have on the floor.
152                    // Also, what behavior we have here really should be based on what protocol
153                    // we're dealing with. If it's POP, then we don't actually know how many
154                    // attachments we have until we've loaded the complete message.
155                    // If it's IMAP, we should know that, and we should load all attachment
156                    // metadata we can get, regardless of whether or not we have the complete
157                    // message body.
158                    att.mFileName = "";
159                    att.mSize = message.getSize();
160                    att.mMimeType = "text/plain";
161                    att.mMessageKey = localMessage.mId;
162                    att.mAccountKey = localMessage.mAccountKey;
163                    att.mFlags = Attachment.FLAG_DUMMY_ATTACHMENT;
164                    att.save(context);
165                    localMessage.mFlagAttachment = true;
166                }
167
168                // One last update of message with two updated flags
169                localMessage.mFlagLoaded = loadStatus;
170
171                ContentValues cv = new ContentValues();
172                cv.put(EmailContent.MessageColumns.FLAG_ATTACHMENT, localMessage.mFlagAttachment);
173                cv.put(EmailContent.MessageColumns.FLAG_LOADED, localMessage.mFlagLoaded);
174                Uri uri = ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI,
175                        localMessage.mId);
176                context.getContentResolver().update(uri, cv, null, null);
177
178            } catch (MessagingException me) {
179                LogUtils.e(Logging.LOG_TAG, "Error while copying downloaded message." + me);
180            }
181
182        } catch (RuntimeException rte) {
183            LogUtils.e(Logging.LOG_TAG, "Error while storing downloaded message." + rte.toString());
184        } catch (IOException ioe) {
185            LogUtils.e(Logging.LOG_TAG, "Error while storing attachment." + ioe.toString());
186        }
187    }
188
189    public static void saveOrUpdate(EmailContent content, Context context) {
190        if (content.isSaved()) {
191            content.update(context, content.toContentValues());
192        } else {
193            content.save(context);
194        }
195    }
196
197}
198