• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 
17 package com.android.email.provider;
18 
19 import android.content.ContentProviderOperation;
20 import android.content.ContentProviderResult;
21 import android.content.ContentResolver;
22 import android.content.ContentUris;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.OperationApplicationException;
26 import android.database.Cursor;
27 import android.net.Uri;
28 import android.os.Environment;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.os.RemoteException;
32 
33 import java.io.File;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.UUID;
39 
40 
41 /**
42  * EmailContent is the superclass of the various classes of content stored by EmailProvider.
43  *
44  * It is intended to include 1) column definitions for use with the Provider, and 2) convenience
45  * methods for saving and retrieving content from the Provider.
46  *
47  * This class will be used by 1) the Email process (which includes the application and
48  * EmaiLProvider) as well as 2) the Exchange process (which runs independently).  It will
49  * necessarily be cloned for use in these two cases.
50  *
51  * Conventions used in naming columns:
52  *   RECORD_ID is the primary key for all Email records
53  *   The SyncColumns interface is used by all classes that are synced to the server directly
54  *   (Mailbox and Email)
55  *
56  *   <name>_KEY always refers to a foreign key
57  *   <name>_ID always refers to a unique identifier (whether on client, server, etc.)
58  *
59  */
60 public abstract class EmailContent {
61     public static final String AUTHORITY = EmailProvider.EMAIL_AUTHORITY;
62     public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
63     // All classes share this
64     public static final String RECORD_ID = "_id";
65 
66     private static final String[] COUNT_COLUMNS = new String[]{"count(*)"};
67 
68     /**
69      * This projection can be used with any of the EmailContent classes, when all you need
70      * is a list of id's.  Use ID_PROJECTION_COLUMN to access the row data.
71      */
72     public static final String[] ID_PROJECTION = new String[] {
73         RECORD_ID
74     };
75     public static final int ID_PROJECTION_COLUMN = 0;
76 
77     private static final String ID_SELECTION = RECORD_ID + " =?";
78 
79     public static final String FIELD_COLUMN_NAME = "field";
80     public static final String ADD_COLUMN_NAME = "add";
81 
82     // Newly created objects get this id
83     private static final int NOT_SAVED = -1;
84     // The base Uri that this piece of content came from
85     public Uri mBaseUri;
86     // Lazily initialized uri for this Content
87     private Uri mUri = null;
88     // The id of the Content
89     public long mId = NOT_SAVED;
90 
91     // Write the Content into a ContentValues container
toContentValues()92     public abstract ContentValues toContentValues();
93     // Read the Content from a ContentCursor
restore(Cursor cursor)94     public abstract <T extends EmailContent> T restore (Cursor cursor);
95 
96     // The Uri is lazily initialized
getUri()97     public Uri getUri() {
98         if (mUri == null) {
99             mUri = ContentUris.withAppendedId(mBaseUri, mId);
100         }
101         return mUri;
102     }
103 
isSaved()104     public boolean isSaved() {
105         return mId != NOT_SAVED;
106     }
107 
108     @SuppressWarnings("unchecked")
109     // The Content sub class must have a no-arg constructor
getContent(Cursor cursor, Class<T> klass)110     static public <T extends EmailContent> T getContent(Cursor cursor, Class<T> klass) {
111         try {
112             T content = klass.newInstance();
113             content.mId = cursor.getLong(0);
114             return (T)content.restore(cursor);
115         } catch (IllegalAccessException e) {
116             e.printStackTrace();
117         } catch (InstantiationException e) {
118             e.printStackTrace();
119         }
120         return null;
121     }
122 
save(Context context)123     public Uri save(Context context) {
124         if (isSaved()) {
125             throw new UnsupportedOperationException();
126         }
127         Uri res = context.getContentResolver().insert(mBaseUri, toContentValues());
128         mId = Long.parseLong(res.getPathSegments().get(1));
129         return res;
130     }
131 
update(Context context, ContentValues contentValues)132     public int update(Context context, ContentValues contentValues) {
133         if (!isSaved()) {
134             throw new UnsupportedOperationException();
135         }
136         return context.getContentResolver().update(getUri(), contentValues, null, null);
137     }
138 
update(Context context, Uri baseUri, long id, ContentValues contentValues)139     static public int update(Context context, Uri baseUri, long id, ContentValues contentValues) {
140         return context.getContentResolver()
141             .update(ContentUris.withAppendedId(baseUri, id), contentValues, null, null);
142     }
143 
144     /**
145      * Generic count method that can be used for any ContentProvider
146      * @param context the calling Context
147      * @param uri the Uri for the provider query
148      * @param selection as with a query call
149      * @param selectionArgs as with a query call
150      * @return the number of items matching the query (or zero)
151      */
count(Context context, Uri uri, String selection, String[] selectionArgs)152     static public int count(Context context, Uri uri, String selection, String[] selectionArgs) {
153         Cursor cursor = context.getContentResolver()
154             .query(uri, COUNT_COLUMNS, selection, selectionArgs, null);
155         try {
156             if (!cursor.moveToFirst()) {
157                 return 0;
158             }
159             return cursor.getInt(0);
160         } finally {
161             cursor.close();
162         }
163     }
164 
165     /**
166      * no public constructor since this is a utility class
167      */
EmailContent()168     private EmailContent() {
169     }
170 
171     public interface SyncColumns {
172         public static final String ID = "_id";
173         // source id (string) : the source's name of this item
174         public static final String SERVER_ID = "syncServerId";
175         // source's timestamp (long) for this item
176         public static final String SERVER_TIMESTAMP = "syncServerTimeStamp";
177     }
178 
179     public interface BodyColumns {
180         public static final String ID = "_id";
181         // Foreign key to the message corresponding to this body
182         public static final String MESSAGE_KEY = "messageKey";
183         // The html content itself
184         public static final String HTML_CONTENT = "htmlContent";
185         // The plain text content itself
186         public static final String TEXT_CONTENT = "textContent";
187         // Replied-to or forwarded body (in html form)
188         public static final String HTML_REPLY = "htmlReply";
189         // Replied-to or forwarded body (in text form)
190         public static final String TEXT_REPLY = "textReply";
191         // A reference to a message's unique id used in reply/forward.
192         // Protocol code can be expected to use this column in determining whether a message can be
193         // deleted safely (i.e. isn't referenced by other messages)
194         public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey";
195         // The text to be placed between a reply/forward response and the original message
196         public static final String INTRO_TEXT = "introText";
197     }
198 
199     public static final class Body extends EmailContent implements BodyColumns {
200         public static final String TABLE_NAME = "Body";
201         public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
202 
203         public static final int CONTENT_ID_COLUMN = 0;
204         public static final int CONTENT_MESSAGE_KEY_COLUMN = 1;
205         public static final int CONTENT_HTML_CONTENT_COLUMN = 2;
206         public static final int CONTENT_TEXT_CONTENT_COLUMN = 3;
207         public static final int CONTENT_HTML_REPLY_COLUMN = 4;
208         public static final int CONTENT_TEXT_REPLY_COLUMN = 5;
209         public static final int CONTENT_SOURCE_KEY_COLUMN = 6;
210         public static final int CONTENT_INTRO_TEXT_COLUMN = 7;
211         public static final String[] CONTENT_PROJECTION = new String[] {
212             RECORD_ID, BodyColumns.MESSAGE_KEY, BodyColumns.HTML_CONTENT, BodyColumns.TEXT_CONTENT,
213             BodyColumns.HTML_REPLY, BodyColumns.TEXT_REPLY, BodyColumns.SOURCE_MESSAGE_KEY,
214             BodyColumns.INTRO_TEXT
215         };
216 
217         public static final String[] COMMON_PROJECTION_TEXT = new String[] {
218             RECORD_ID, BodyColumns.TEXT_CONTENT
219         };
220         public static final String[] COMMON_PROJECTION_HTML = new String[] {
221             RECORD_ID, BodyColumns.HTML_CONTENT
222         };
223         public static final String[] COMMON_PROJECTION_REPLY_TEXT = new String[] {
224             RECORD_ID, BodyColumns.TEXT_REPLY
225         };
226         public static final String[] COMMON_PROJECTION_REPLY_HTML = new String[] {
227             RECORD_ID, BodyColumns.HTML_REPLY
228         };
229         public static final String[] COMMON_PROJECTION_INTRO = new String[] {
230             RECORD_ID, BodyColumns.INTRO_TEXT
231         };
232         public static final int COMMON_PROJECTION_COLUMN_TEXT = 1;
233 
234         private static final String[] PROJECTION_SOURCE_KEY =
235             new String[] { BodyColumns.SOURCE_MESSAGE_KEY };
236 
237         public long mMessageKey;
238         public String mHtmlContent;
239         public String mTextContent;
240         public String mHtmlReply;
241         public String mTextReply;
242         public long mSourceKey;
243         public String mIntroText;
244 
Body()245         public Body() {
246             mBaseUri = CONTENT_URI;
247         }
248 
249         @Override
toContentValues()250         public ContentValues toContentValues() {
251             ContentValues values = new ContentValues();
252 
253             // Assign values for each row.
254             values.put(BodyColumns.MESSAGE_KEY, mMessageKey);
255             values.put(BodyColumns.HTML_CONTENT, mHtmlContent);
256             values.put(BodyColumns.TEXT_CONTENT, mTextContent);
257             values.put(BodyColumns.HTML_REPLY, mHtmlReply);
258             values.put(BodyColumns.TEXT_REPLY, mTextReply);
259             values.put(BodyColumns.SOURCE_MESSAGE_KEY, mSourceKey);
260             values.put(BodyColumns.INTRO_TEXT, mIntroText);
261             return values;
262         }
263 
restoreBodyWithCursor(Cursor cursor)264         private static Body restoreBodyWithCursor(Cursor cursor) {
265             try {
266                 if (cursor.moveToFirst()) {
267                     return getContent(cursor, Body.class);
268                 } else {
269                     return null;
270                 }
271             } finally {
272                 cursor.close();
273             }
274         }
275 
restoreBodyWithId(Context context, long id)276         public static Body restoreBodyWithId(Context context, long id) {
277             Uri u = ContentUris.withAppendedId(Body.CONTENT_URI, id);
278             Cursor c = context.getContentResolver().query(u, Body.CONTENT_PROJECTION,
279                     null, null, null);
280             return restoreBodyWithCursor(c);
281         }
282 
restoreBodyWithMessageId(Context context, long messageId)283         public static Body restoreBodyWithMessageId(Context context, long messageId) {
284             Cursor c = context.getContentResolver().query(Body.CONTENT_URI,
285                     Body.CONTENT_PROJECTION, Body.MESSAGE_KEY + "=?",
286                     new String[] {Long.toString(messageId)}, null);
287             return restoreBodyWithCursor(c);
288         }
289 
290         /**
291          * Returns the bodyId for the given messageId, or -1 if no body is found.
292          */
lookupBodyIdWithMessageId(ContentResolver resolver, long messageId)293         public static long lookupBodyIdWithMessageId(ContentResolver resolver, long messageId) {
294             Cursor c = resolver.query(Body.CONTENT_URI, ID_PROJECTION,
295                     Body.MESSAGE_KEY + "=?",
296                     new String[] {Long.toString(messageId)}, null);
297             try {
298                 return c.moveToFirst() ? c.getLong(ID_PROJECTION_COLUMN) : -1;
299             } finally {
300                 c.close();
301             }
302         }
303 
304         /**
305          * Updates the Body for a messageId with the given ContentValues.
306          * If the message has no body, a new body is inserted for the message.
307          * Warning: the argument "values" is modified by this method, setting MESSAGE_KEY.
308          */
updateBodyWithMessageId(Context context, long messageId, ContentValues values)309         public static void updateBodyWithMessageId(Context context, long messageId,
310                 ContentValues values) {
311             ContentResolver resolver = context.getContentResolver();
312             long bodyId = lookupBodyIdWithMessageId(resolver, messageId);
313             values.put(BodyColumns.MESSAGE_KEY, messageId);
314             if (bodyId == -1) {
315                 resolver.insert(CONTENT_URI, values);
316             } else {
317                 final Uri uri = ContentUris.withAppendedId(CONTENT_URI, bodyId);
318                 resolver.update(uri, values, null, null);
319             }
320         }
321 
restoreBodySourceKey(Context context, long messageId)322         public static long restoreBodySourceKey(Context context, long messageId) {
323             Cursor c = context.getContentResolver().query(Body.CONTENT_URI,
324                     Body.PROJECTION_SOURCE_KEY,
325                     Body.MESSAGE_KEY + "=?", new String[] {Long.toString(messageId)}, null);
326             try {
327                 if (c.moveToFirst()) {
328                     return c.getLong(0);
329                 } else {
330                     return 0;
331                 }
332             } finally {
333                 c.close();
334             }
335         }
336 
restoreTextWithMessageId(Context context, long messageId, String[] projection)337         private static String restoreTextWithMessageId(Context context, long messageId,
338                 String[] projection) {
339             Cursor c = context.getContentResolver().query(Body.CONTENT_URI, projection,
340                     Body.MESSAGE_KEY + "=?", new String[] {Long.toString(messageId)}, null);
341             try {
342                 if (c.moveToFirst()) {
343                     return c.getString(COMMON_PROJECTION_COLUMN_TEXT);
344                 } else {
345                     return null;
346                 }
347             } finally {
348                 c.close();
349             }
350         }
351 
restoreBodyTextWithMessageId(Context context, long messageId)352         public static String restoreBodyTextWithMessageId(Context context, long messageId) {
353             return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_TEXT);
354         }
355 
restoreBodyHtmlWithMessageId(Context context, long messageId)356         public static String restoreBodyHtmlWithMessageId(Context context, long messageId) {
357             return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_HTML);
358         }
359 
restoreReplyTextWithMessageId(Context context, long messageId)360         public static String restoreReplyTextWithMessageId(Context context, long messageId) {
361             return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_TEXT);
362         }
363 
restoreReplyHtmlWithMessageId(Context context, long messageId)364         public static String restoreReplyHtmlWithMessageId(Context context, long messageId) {
365             return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_HTML);
366         }
367 
restoreIntroTextWithMessageId(Context context, long messageId)368         public static String restoreIntroTextWithMessageId(Context context, long messageId) {
369             return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO);
370         }
371 
372         @Override
373         @SuppressWarnings("unchecked")
restore(Cursor c)374         public EmailContent.Body restore(Cursor c) {
375             mBaseUri = EmailContent.Body.CONTENT_URI;
376             mMessageKey = c.getLong(CONTENT_MESSAGE_KEY_COLUMN);
377             mHtmlContent = c.getString(CONTENT_HTML_CONTENT_COLUMN);
378             mTextContent = c.getString(CONTENT_TEXT_CONTENT_COLUMN);
379             mHtmlReply = c.getString(CONTENT_HTML_REPLY_COLUMN);
380             mTextReply = c.getString(CONTENT_TEXT_REPLY_COLUMN);
381             mSourceKey = c.getLong(CONTENT_SOURCE_KEY_COLUMN);
382             mIntroText = c.getString(CONTENT_INTRO_TEXT_COLUMN);
383             return this;
384         }
385 
update()386         public boolean update() {
387             // TODO Auto-generated method stub
388             return false;
389         }
390     }
391 
392     public interface MessageColumns {
393         public static final String ID = "_id";
394         // Basic columns used in message list presentation
395         // The name as shown to the user in a message list
396         public static final String DISPLAY_NAME = "displayName";
397         // The time (millis) as shown to the user in a message list [INDEX]
398         public static final String TIMESTAMP = "timeStamp";
399         // Message subject
400         public static final String SUBJECT = "subject";
401         // Boolean, unread = 0, read = 1 [INDEX]
402         public static final String FLAG_READ = "flagRead";
403         // Load state, see constants below (unloaded, partial, complete, deleted)
404         public static final String FLAG_LOADED = "flagLoaded";
405         // Boolean, unflagged = 0, flagged (favorite) = 1
406         public static final String FLAG_FAVORITE = "flagFavorite";
407         // Boolean, no attachment = 0, attachment = 1
408         public static final String FLAG_ATTACHMENT = "flagAttachment";
409         // Bit field for flags which we'll not be selecting on
410         public static final String FLAGS = "flags";
411 
412         // Sync related identifiers
413         // Any client-required identifier
414         public static final String CLIENT_ID = "clientId";
415         // The message-id in the message's header
416         public static final String MESSAGE_ID = "messageId";
417 
418         // References to other Email objects in the database
419         // Foreign key to the Mailbox holding this message [INDEX]
420         public static final String MAILBOX_KEY = "mailboxKey";
421         // Foreign key to the Account holding this message
422         public static final String ACCOUNT_KEY = "accountKey";
423 
424         // Address lists, packed with Address.pack()
425         public static final String FROM_LIST = "fromList";
426         public static final String TO_LIST = "toList";
427         public static final String CC_LIST = "ccList";
428         public static final String BCC_LIST = "bccList";
429         public static final String REPLY_TO_LIST = "replyToList";
430 
431         // Meeting invitation related information (for now, start time in ms)
432         public static final String MEETING_INFO = "meetingInfo";
433     }
434 
435     public static final class Message extends EmailContent implements SyncColumns, MessageColumns {
436         public static final String TABLE_NAME = "Message";
437         public static final String UPDATED_TABLE_NAME = "Message_Updates";
438         public static final String DELETED_TABLE_NAME = "Message_Deletes";
439 
440         // To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id)
441         public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
442         public static final Uri SYNCED_CONTENT_URI =
443             Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
444         public static final Uri DELETED_CONTENT_URI =
445             Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
446         public static final Uri UPDATED_CONTENT_URI =
447             Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
448 
449         public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc";
450 
451         public static final int CONTENT_ID_COLUMN = 0;
452         public static final int CONTENT_DISPLAY_NAME_COLUMN = 1;
453         public static final int CONTENT_TIMESTAMP_COLUMN = 2;
454         public static final int CONTENT_SUBJECT_COLUMN = 3;
455         public static final int CONTENT_FLAG_READ_COLUMN = 4;
456         public static final int CONTENT_FLAG_LOADED_COLUMN = 5;
457         public static final int CONTENT_FLAG_FAVORITE_COLUMN = 6;
458         public static final int CONTENT_FLAG_ATTACHMENT_COLUMN = 7;
459         public static final int CONTENT_FLAGS_COLUMN = 8;
460         public static final int CONTENT_SERVER_ID_COLUMN = 9;
461         public static final int CONTENT_CLIENT_ID_COLUMN = 10;
462         public static final int CONTENT_MESSAGE_ID_COLUMN = 11;
463         public static final int CONTENT_MAILBOX_KEY_COLUMN = 12;
464         public static final int CONTENT_ACCOUNT_KEY_COLUMN = 13;
465         public static final int CONTENT_FROM_LIST_COLUMN = 14;
466         public static final int CONTENT_TO_LIST_COLUMN = 15;
467         public static final int CONTENT_CC_LIST_COLUMN = 16;
468         public static final int CONTENT_BCC_LIST_COLUMN = 17;
469         public static final int CONTENT_REPLY_TO_COLUMN = 18;
470         public static final int CONTENT_SERVER_TIMESTAMP_COLUMN = 19;
471         public static final int CONTENT_MEETING_INFO_COLUMN = 20;
472 
473         public static final String[] CONTENT_PROJECTION = new String[] {
474             RECORD_ID,
475             MessageColumns.DISPLAY_NAME, MessageColumns.TIMESTAMP,
476             MessageColumns.SUBJECT, MessageColumns.FLAG_READ,
477             MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE,
478             MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS,
479             SyncColumns.SERVER_ID, MessageColumns.CLIENT_ID,
480             MessageColumns.MESSAGE_ID, MessageColumns.MAILBOX_KEY,
481             MessageColumns.ACCOUNT_KEY, MessageColumns.FROM_LIST,
482             MessageColumns.TO_LIST, MessageColumns.CC_LIST,
483             MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST,
484             SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO
485         };
486 
487         public static final int LIST_ID_COLUMN = 0;
488         public static final int LIST_DISPLAY_NAME_COLUMN = 1;
489         public static final int LIST_TIMESTAMP_COLUMN = 2;
490         public static final int LIST_SUBJECT_COLUMN = 3;
491         public static final int LIST_READ_COLUMN = 4;
492         public static final int LIST_LOADED_COLUMN = 5;
493         public static final int LIST_FAVORITE_COLUMN = 6;
494         public static final int LIST_ATTACHMENT_COLUMN = 7;
495         public static final int LIST_FLAGS_COLUMN = 8;
496         public static final int LIST_MAILBOX_KEY_COLUMN = 9;
497         public static final int LIST_ACCOUNT_KEY_COLUMN = 10;
498         public static final int LIST_SERVER_ID_COLUMN = 11;
499 
500         // Public projection for common list columns
501         public static final String[] LIST_PROJECTION = new String[] {
502             RECORD_ID,
503             MessageColumns.DISPLAY_NAME, MessageColumns.TIMESTAMP,
504             MessageColumns.SUBJECT, MessageColumns.FLAG_READ,
505             MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE,
506             MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS,
507             MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY,
508             SyncColumns.SERVER_ID
509         };
510 
511         public static final int ID_COLUMNS_ID_COLUMN = 0;
512         public static final int ID_COLUMNS_SYNC_SERVER_ID = 1;
513         public static final String[] ID_COLUMNS_PROJECTION = new String[] {
514             RECORD_ID, SyncColumns.SERVER_ID
515         };
516 
517         public static final int ID_MAILBOX_COLUMN_ID = 0;
518         public static final int ID_MAILBOX_COLUMN_MAILBOX_KEY = 1;
519         public static final String[] ID_MAILBOX_PROJECTION = new String[] {
520             RECORD_ID, MessageColumns.MAILBOX_KEY
521         };
522 
523         public static final String[] ID_COLUMN_PROJECTION = new String[] { RECORD_ID };
524 
525         // _id field is in AbstractContent
526         public String mDisplayName;
527         public long mTimeStamp;
528         public String mSubject;
529         public boolean mFlagRead = false;
530         public int mFlagLoaded = FLAG_LOADED_UNLOADED;
531         public boolean mFlagFavorite = false;
532         public boolean mFlagAttachment = false;
533         public int mFlags = 0;
534 
535         public String mServerId;
536         public long mServerTimeStamp;
537         public String mClientId;
538         public String mMessageId;
539 
540         public long mMailboxKey;
541         public long mAccountKey;
542 
543         public String mFrom;
544         public String mTo;
545         public String mCc;
546         public String mBcc;
547         public String mReplyTo;
548 
549         // For now, just the start time of a meeting invite, in ms
550         public String mMeetingInfo;
551 
552         // The following transient members may be used while building and manipulating messages,
553         // but they are NOT persisted directly by EmailProvider
554         transient public String mText;
555         transient public String mHtml;
556         transient public String mTextReply;
557         transient public String mHtmlReply;
558         transient public long mSourceKey;
559         transient public ArrayList<Attachment> mAttachments = null;
560         transient public String mIntroText;
561 
562         // Values used in mFlagRead
563         public static final int UNREAD = 0;
564         public static final int READ = 1;
565 
566         // Values used in mFlagLoaded
567         public static final int FLAG_LOADED_UNLOADED = 0;
568         public static final int FLAG_LOADED_COMPLETE = 1;
569         public static final int FLAG_LOADED_PARTIAL = 2;
570         public static final int FLAG_LOADED_DELETED = 3;
571 
572         // Bits used in mFlags
573         // The following three states are mutually exclusive, and indicate whether the message is an
574         // original, a reply, or a forward
575         public static final int FLAG_TYPE_ORIGINAL = 0;
576         public static final int FLAG_TYPE_REPLY = 1<<0;
577         public static final int FLAG_TYPE_FORWARD = 1<<1;
578         public static final int FLAG_TYPE_MASK = FLAG_TYPE_REPLY | FLAG_TYPE_FORWARD;
579         // The following flags indicate messages that are determined to be incoming meeting related
580         // (e.g. invites from others)
581         public static final int FLAG_INCOMING_MEETING_INVITE = 1<<2;
582         public static final int FLAG_INCOMING_MEETING_CANCEL = 1<<3;
583         public static final int FLAG_INCOMING_MEETING_MASK =
584             FLAG_INCOMING_MEETING_INVITE | FLAG_INCOMING_MEETING_CANCEL;
585         // The following flags indicate messages that are outgoing and meeting related
586         // (e.g. invites TO others)
587         public static final int FLAG_OUTGOING_MEETING_INVITE = 1<<4;
588         public static final int FLAG_OUTGOING_MEETING_CANCEL = 1<<5;
589         public static final int FLAG_OUTGOING_MEETING_ACCEPT = 1<<6;
590         public static final int FLAG_OUTGOING_MEETING_DECLINE = 1<<7;
591         public static final int FLAG_OUTGOING_MEETING_TENTATIVE = 1<<8;
592         public static final int FLAG_OUTGOING_MEETING_MASK =
593             FLAG_OUTGOING_MEETING_INVITE | FLAG_OUTGOING_MEETING_CANCEL |
594             FLAG_OUTGOING_MEETING_ACCEPT | FLAG_OUTGOING_MEETING_DECLINE |
595             FLAG_OUTGOING_MEETING_TENTATIVE;
596         public static final int FLAG_OUTGOING_MEETING_REQUEST_MASK =
597             FLAG_OUTGOING_MEETING_INVITE | FLAG_OUTGOING_MEETING_CANCEL;
598 
Message()599         public Message() {
600             mBaseUri = CONTENT_URI;
601         }
602 
603         @Override
toContentValues()604         public ContentValues toContentValues() {
605             ContentValues values = new ContentValues();
606 
607             // Assign values for each row.
608             values.put(MessageColumns.DISPLAY_NAME, mDisplayName);
609             values.put(MessageColumns.TIMESTAMP, mTimeStamp);
610             values.put(MessageColumns.SUBJECT, mSubject);
611             values.put(MessageColumns.FLAG_READ, mFlagRead);
612             values.put(MessageColumns.FLAG_LOADED, mFlagLoaded);
613             values.put(MessageColumns.FLAG_FAVORITE, mFlagFavorite);
614             values.put(MessageColumns.FLAG_ATTACHMENT, mFlagAttachment);
615             values.put(MessageColumns.FLAGS, mFlags);
616 
617             values.put(SyncColumns.SERVER_ID, mServerId);
618             values.put(SyncColumns.SERVER_TIMESTAMP, mServerTimeStamp);
619             values.put(MessageColumns.CLIENT_ID, mClientId);
620             values.put(MessageColumns.MESSAGE_ID, mMessageId);
621 
622             values.put(MessageColumns.MAILBOX_KEY, mMailboxKey);
623             values.put(MessageColumns.ACCOUNT_KEY, mAccountKey);
624 
625             values.put(MessageColumns.FROM_LIST, mFrom);
626             values.put(MessageColumns.TO_LIST, mTo);
627             values.put(MessageColumns.CC_LIST, mCc);
628             values.put(MessageColumns.BCC_LIST, mBcc);
629             values.put(MessageColumns.REPLY_TO_LIST, mReplyTo);
630 
631             values.put(MessageColumns.MEETING_INFO, mMeetingInfo);
632 
633             return values;
634         }
635 
restoreMessageWithId(Context context, long id)636         public static Message restoreMessageWithId(Context context, long id) {
637             Uri u = ContentUris.withAppendedId(Message.CONTENT_URI, id);
638             Cursor c = context.getContentResolver().query(u, Message.CONTENT_PROJECTION,
639                     null, null, null);
640 
641             try {
642                 if (c.moveToFirst()) {
643                     return getContent(c, Message.class);
644                 } else {
645                     return null;
646                 }
647             } finally {
648                 c.close();
649             }
650         }
651 
652         @Override
653         @SuppressWarnings("unchecked")
restore(Cursor c)654         public EmailContent.Message restore(Cursor c) {
655             mBaseUri = CONTENT_URI;
656             mId = c.getLong(CONTENT_ID_COLUMN);
657             mDisplayName = c.getString(CONTENT_DISPLAY_NAME_COLUMN);
658             mTimeStamp = c.getLong(CONTENT_TIMESTAMP_COLUMN);
659             mSubject = c.getString(CONTENT_SUBJECT_COLUMN);
660             mFlagRead = c.getInt(CONTENT_FLAG_READ_COLUMN) == 1;
661             mFlagLoaded = c.getInt(CONTENT_FLAG_LOADED_COLUMN);
662             mFlagFavorite = c.getInt(CONTENT_FLAG_FAVORITE_COLUMN) == 1;
663             mFlagAttachment = c.getInt(CONTENT_FLAG_ATTACHMENT_COLUMN) == 1;
664             mFlags = c.getInt(CONTENT_FLAGS_COLUMN);
665             mServerId = c.getString(CONTENT_SERVER_ID_COLUMN);
666             mServerTimeStamp = c.getLong(CONTENT_SERVER_TIMESTAMP_COLUMN);
667             mClientId = c.getString(CONTENT_CLIENT_ID_COLUMN);
668             mMessageId = c.getString(CONTENT_MESSAGE_ID_COLUMN);
669             mMailboxKey = c.getLong(CONTENT_MAILBOX_KEY_COLUMN);
670             mAccountKey = c.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
671             mFrom = c.getString(CONTENT_FROM_LIST_COLUMN);
672             mTo = c.getString(CONTENT_TO_LIST_COLUMN);
673             mCc = c.getString(CONTENT_CC_LIST_COLUMN);
674             mBcc = c.getString(CONTENT_BCC_LIST_COLUMN);
675             mReplyTo = c.getString(CONTENT_REPLY_TO_COLUMN);
676             mMeetingInfo = c.getString(CONTENT_MEETING_INFO_COLUMN);
677             return this;
678         }
679 
update()680         public boolean update() {
681             // TODO Auto-generated method stub
682             return false;
683         }
684 
685         /*
686          * Override this so that we can store the Body first and link it to the Message
687          * Also, attachments when we get there...
688          * (non-Javadoc)
689          * @see com.android.email.provider.EmailContent#save(android.content.Context)
690          */
691         @Override
save(Context context)692         public Uri save(Context context) {
693 
694             boolean doSave = !isSaved();
695 
696             // This logic is in place so I can (a) short circuit the expensive stuff when
697             // possible, and (b) override (and throw) if anyone tries to call save() or update()
698             // directly for Message, which are unsupported.
699             if (mText == null && mHtml == null && mTextReply == null && mHtmlReply == null &&
700                     (mAttachments == null || mAttachments.isEmpty())) {
701                 if (doSave) {
702                     return super.save(context);
703                 } else {
704                     // Call update, rather than super.update in case we ever override it
705                     if (update(context, toContentValues()) == 1) {
706                         return getUri();
707                     }
708                     return null;
709                 }
710             }
711 
712             ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
713             addSaveOps(ops);
714             try {
715                 ContentProviderResult[] results =
716                     context.getContentResolver().applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
717                 // If saving, set the mId's of the various saved objects
718                 if (doSave) {
719                     Uri u = results[0].uri;
720                     mId = Long.parseLong(u.getPathSegments().get(1));
721                     if (mAttachments != null) {
722                         int resultOffset = 2;
723                         for (Attachment a : mAttachments) {
724                             // Save the id of the attachment record
725                             u = results[resultOffset++].uri;
726                             if (u != null) {
727                                 a.mId = Long.parseLong(u.getPathSegments().get(1));
728                             }
729                             a.mMessageKey = mId;
730                         }
731                     }
732                     return u;
733                 } else {
734                     return null;
735                 }
736             } catch (RemoteException e) {
737                 // There is nothing to be done here; fail by returning null
738             } catch (OperationApplicationException e) {
739                 // There is nothing to be done here; fail by returning null
740             }
741             return null;
742         }
743 
addSaveOps(ArrayList<ContentProviderOperation> ops)744         public void addSaveOps(ArrayList<ContentProviderOperation> ops) {
745             // First, save the message
746             ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(mBaseUri);
747             ops.add(b.withValues(toContentValues()).build());
748 
749             // Create and save the body
750             ContentValues cv = new ContentValues();
751             if (mText != null) {
752                 cv.put(Body.TEXT_CONTENT, mText);
753             }
754             if (mHtml != null) {
755                 cv.put(Body.HTML_CONTENT, mHtml);
756             }
757             if (mTextReply != null) {
758                 cv.put(Body.TEXT_REPLY, mTextReply);
759             }
760             if (mHtmlReply != null) {
761                 cv.put(Body.HTML_REPLY, mHtmlReply);
762             }
763             if (mSourceKey != 0) {
764                 cv.put(Body.SOURCE_MESSAGE_KEY, mSourceKey);
765             }
766             if (mIntroText != null) {
767                 cv.put(Body.INTRO_TEXT, mIntroText);
768             }
769             b = ContentProviderOperation.newInsert(Body.CONTENT_URI);
770             b.withValues(cv);
771             ContentValues backValues = new ContentValues();
772             int messageBackValue = ops.size() - 1;
773             backValues.put(Body.MESSAGE_KEY, messageBackValue);
774             ops.add(b.withValueBackReferences(backValues).build());
775 
776             // Create the attaachments, if any
777             if (mAttachments != null) {
778                 for (Attachment att: mAttachments) {
779                     ops.add(ContentProviderOperation.newInsert(Attachment.CONTENT_URI)
780                         .withValues(att.toContentValues())
781                         .withValueBackReference(Attachment.MESSAGE_KEY, messageBackValue)
782                         .build());
783                 }
784             }
785         }
786     }
787 
788     public interface AccountColumns {
789         public static final String ID = "_id";
790         // The display name of the account (user-settable)
791         public static final String DISPLAY_NAME = "displayName";
792         // The email address corresponding to this account
793         public static final String EMAIL_ADDRESS = "emailAddress";
794         // A server-based sync key on an account-wide basis (EAS needs this)
795         public static final String SYNC_KEY = "syncKey";
796         // The default sync lookback period for this account
797         public static final String SYNC_LOOKBACK = "syncLookback";
798         // The default sync frequency for this account, in minutes
799         public static final String SYNC_INTERVAL = "syncInterval";
800         // A foreign key into the account manager, having host, login, password, port, and ssl flags
801         public static final String HOST_AUTH_KEY_RECV = "hostAuthKeyRecv";
802         // (optional) A foreign key into the account manager, having host, login, password, port,
803         // and ssl flags
804         public static final String HOST_AUTH_KEY_SEND = "hostAuthKeySend";
805         // Flags
806         public static final String FLAGS = "flags";
807         // Default account
808         public static final String IS_DEFAULT = "isDefault";
809         // Old-Style UUID for compatibility with previous versions
810         public static final String COMPATIBILITY_UUID = "compatibilityUuid";
811         // User name (for outgoing messages)
812         public static final String SENDER_NAME = "senderName";
813         // Ringtone
814         public static final String RINGTONE_URI = "ringtoneUri";
815         // Protocol version (arbitrary string, used by EAS currently)
816         public static final String PROTOCOL_VERSION = "protocolVersion";
817         // The number of new messages (reported by the sync/download engines
818         public static final String NEW_MESSAGE_COUNT = "newMessageCount";
819         // Flags defining security (provisioning) requirements of this account
820         public static final String SECURITY_FLAGS = "securityFlags";
821         // Server-based sync key for the security policies currently enforced
822         public static final String SECURITY_SYNC_KEY = "securitySyncKey";
823         // Signature to use with this account
824         public static final String SIGNATURE = "signature";
825     }
826 
827     public static final class Account extends EmailContent implements AccountColumns, Parcelable {
828         public static final String TABLE_NAME = "Account";
829         public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account");
830         public static final Uri ADD_TO_FIELD_URI =
831             Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField");
832 
833         public final static int FLAGS_NOTIFY_NEW_MAIL = 1;
834         public final static int FLAGS_VIBRATE_ALWAYS = 2;
835         public static final int FLAGS_DELETE_POLICY_MASK = 4+8;
836         public static final int FLAGS_DELETE_POLICY_SHIFT = 2;
837         public static final int FLAGS_INCOMPLETE = 16;
838         public static final int FLAGS_SECURITY_HOLD = 32;
839         public static final int FLAGS_VIBRATE_WHEN_SILENT = 64;
840 
841         public static final int DELETE_POLICY_NEVER = 0;
842         public static final int DELETE_POLICY_7DAYS = 1;        // not supported
843         public static final int DELETE_POLICY_ON_DELETE = 2;
844 
845         // Sentinel values for the mSyncInterval field of both Account records
846         public static final int CHECK_INTERVAL_NEVER = -1;
847         public static final int CHECK_INTERVAL_PUSH = -2;
848 
849         public String mDisplayName;
850         public String mEmailAddress;
851         public String mSyncKey;
852         public int mSyncLookback;
853         public int mSyncInterval;
854         public long mHostAuthKeyRecv;
855         public long mHostAuthKeySend;
856         public int mFlags;
857         public boolean mIsDefault;          // note: callers should use getDefaultAccountId()
858         public String mCompatibilityUuid;
859         public String mSenderName;
860         public String mRingtoneUri;
861         public String mProtocolVersion;
862         public int mNewMessageCount;
863         public int mSecurityFlags;
864         public String mSecuritySyncKey;
865         public String mSignature;
866 
867         // Convenience for creating an account
868         public transient HostAuth mHostAuthRecv;
869         public transient HostAuth mHostAuthSend;
870 
871         public static final int CONTENT_ID_COLUMN = 0;
872         public static final int CONTENT_DISPLAY_NAME_COLUMN = 1;
873         public static final int CONTENT_EMAIL_ADDRESS_COLUMN = 2;
874         public static final int CONTENT_SYNC_KEY_COLUMN = 3;
875         public static final int CONTENT_SYNC_LOOKBACK_COLUMN = 4;
876         public static final int CONTENT_SYNC_INTERVAL_COLUMN = 5;
877         public static final int CONTENT_HOST_AUTH_KEY_RECV_COLUMN = 6;
878         public static final int CONTENT_HOST_AUTH_KEY_SEND_COLUMN = 7;
879         public static final int CONTENT_FLAGS_COLUMN = 8;
880         public static final int CONTENT_IS_DEFAULT_COLUMN = 9;
881         public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 10;
882         public static final int CONTENT_SENDER_NAME_COLUMN = 11;
883         public static final int CONTENT_RINGTONE_URI_COLUMN = 12;
884         public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 13;
885         public static final int CONTENT_NEW_MESSAGE_COUNT_COLUMN = 14;
886         public static final int CONTENT_SECURITY_FLAGS_COLUMN = 15;
887         public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 16;
888         public static final int CONTENT_SIGNATURE_COLUMN = 17;
889 
890         public static final String[] CONTENT_PROJECTION = new String[] {
891             RECORD_ID, AccountColumns.DISPLAY_NAME,
892             AccountColumns.EMAIL_ADDRESS, AccountColumns.SYNC_KEY, AccountColumns.SYNC_LOOKBACK,
893             AccountColumns.SYNC_INTERVAL, AccountColumns.HOST_AUTH_KEY_RECV,
894             AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, AccountColumns.IS_DEFAULT,
895             AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME,
896             AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION,
897             AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_FLAGS,
898             AccountColumns.SECURITY_SYNC_KEY, AccountColumns.SIGNATURE
899         };
900 
901         public static final int CONTENT_MAILBOX_TYPE_COLUMN = 1;
902 
903         /**
904          * This projection is for listing account id's only
905          */
906         public static final String[] ID_TYPE_PROJECTION = new String[] {
907             RECORD_ID, MailboxColumns.TYPE
908         };
909 
910         public static final String MAILBOX_SELECTION =
911             MessageColumns.MAILBOX_KEY + " =?";
912 
913         public static final String UNREAD_COUNT_SELECTION =
914             MessageColumns.MAILBOX_KEY + " =? and " + MessageColumns.FLAG_READ + "= 0";
915 
916         public static final String UUID_SELECTION = AccountColumns.COMPATIBILITY_UUID + " =?";
917 
918         /**
919          * This projection is for searching for the default account
920          */
921         private static final String[] DEFAULT_ID_PROJECTION = new String[] {
922             RECORD_ID, IS_DEFAULT
923         };
924 
925         /**
926          * no public constructor since this is a utility class
927          */
Account()928         public Account() {
929             mBaseUri = CONTENT_URI;
930 
931             // other defaults (policy)
932             mRingtoneUri = "content://settings/system/notification_sound";
933             mSyncInterval = -1;
934             mSyncLookback = -1;
935             mFlags = FLAGS_NOTIFY_NEW_MAIL;
936             mCompatibilityUuid = UUID.randomUUID().toString();
937         }
938 
restoreAccountWithId(Context context, long id)939         public static Account restoreAccountWithId(Context context, long id) {
940             Uri u = ContentUris.withAppendedId(Account.CONTENT_URI, id);
941             Cursor c = context.getContentResolver().query(u, Account.CONTENT_PROJECTION,
942                     null, null, null);
943 
944             try {
945                 if (c.moveToFirst()) {
946                     return getContent(c, Account.class);
947                 } else {
948                     return null;
949                 }
950             } finally {
951                 c.close();
952             }
953         }
954 
955         /**
956          * Refresh an account that has already been loaded.  This is slightly less expensive
957          * that generating a brand-new account object.
958          */
refresh(Context context)959         public void refresh(Context context) {
960             Cursor c = context.getContentResolver().query(this.getUri(), Account.CONTENT_PROJECTION,
961                     null, null, null);
962             try {
963                 c.moveToFirst();
964                 restore(c);
965             } finally {
966                 if (c != null) {
967                     c.close();
968                 }
969             }
970         }
971 
972         @Override
973         @SuppressWarnings("unchecked")
restore(Cursor cursor)974         public EmailContent.Account restore(Cursor cursor) {
975             mId = cursor.getLong(CONTENT_ID_COLUMN);
976             mBaseUri = CONTENT_URI;
977             mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN);
978             mEmailAddress = cursor.getString(CONTENT_EMAIL_ADDRESS_COLUMN);
979             mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN);
980             mSyncLookback = cursor.getInt(CONTENT_SYNC_LOOKBACK_COLUMN);
981             mSyncInterval = cursor.getInt(CONTENT_SYNC_INTERVAL_COLUMN);
982             mHostAuthKeyRecv = cursor.getLong(CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
983             mHostAuthKeySend = cursor.getLong(CONTENT_HOST_AUTH_KEY_SEND_COLUMN);
984             mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
985             mIsDefault = cursor.getInt(CONTENT_IS_DEFAULT_COLUMN) == 1;
986             mCompatibilityUuid = cursor.getString(CONTENT_COMPATIBILITY_UUID_COLUMN);
987             mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN);
988             mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN);
989             mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN);
990             mNewMessageCount = cursor.getInt(CONTENT_NEW_MESSAGE_COUNT_COLUMN);
991             mSecurityFlags = cursor.getInt(CONTENT_SECURITY_FLAGS_COLUMN);
992             mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN);
993             mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN);
994             return this;
995         }
996 
getId(Uri u)997         private long getId(Uri u) {
998             return Long.parseLong(u.getPathSegments().get(1));
999         }
1000 
1001         /**
1002          * @return the user-visible name for the account
1003          */
getDisplayName()1004         public String getDisplayName() {
1005             return mDisplayName;
1006         }
1007 
1008         /**
1009          * Set the description.  Be sure to call save() to commit to database.
1010          * @param description the new description
1011          */
setDisplayName(String description)1012         public void setDisplayName(String description) {
1013             mDisplayName = description;
1014         }
1015 
1016         /**
1017          * @return the email address for this account
1018          */
getEmailAddress()1019         public String getEmailAddress() {
1020             return mEmailAddress;
1021         }
1022 
1023         /**
1024          * Set the Email address for this account.  Be sure to call save() to commit to database.
1025          * @param emailAddress the new email address for this account
1026          */
setEmailAddress(String emailAddress)1027         public void setEmailAddress(String emailAddress) {
1028             mEmailAddress = emailAddress;
1029         }
1030 
1031         /**
1032          * @return the sender's name for this account
1033          */
getSenderName()1034         public String getSenderName() {
1035             return mSenderName;
1036         }
1037 
1038         /**
1039          * Set the sender's name.  Be sure to call save() to commit to database.
1040          * @param name the new sender name
1041          */
setSenderName(String name)1042         public void setSenderName(String name) {
1043             mSenderName = name;
1044         }
1045 
getSignature()1046         public String getSignature() {
1047             return mSignature;
1048         }
1049 
setSignature(String signature)1050         public void setSignature(String signature) {
1051             mSignature = signature;
1052         }
1053 
1054 
1055         /**
1056          * @return the minutes per check (for polling)
1057          * TODO define sentinel values for "never", "push", etc.  See Account.java
1058          */
getSyncInterval()1059         public int getSyncInterval()
1060         {
1061             return mSyncInterval;
1062         }
1063 
1064         /**
1065          * Set the minutes per check (for polling).  Be sure to call save() to commit to database.
1066          * TODO define sentinel values for "never", "push", etc.  See Account.java
1067          * @param minutes the number of minutes between polling checks
1068          */
setSyncInterval(int minutes)1069         public void setSyncInterval(int minutes)
1070         {
1071             mSyncInterval = minutes;
1072         }
1073 
1074         /**
1075          * @return One of the {@code Account.SYNC_WINDOW_*} constants that represents the sync
1076          *     lookback window.
1077          * TODO define sentinel values for "all", "1 month", etc.  See Account.java
1078          */
getSyncLookback()1079         public int getSyncLookback() {
1080             return mSyncLookback;
1081         }
1082 
1083         /**
1084          * Set the sync lookback window.  Be sure to call save() to commit to database.
1085          * TODO define sentinel values for "all", "1 month", etc.  See Account.java
1086          * @param value One of the {@code Account.SYNC_WINDOW_*} constants
1087          */
setSyncLookback(int value)1088         public void setSyncLookback(int value) {
1089             mSyncLookback = value;
1090         }
1091 
1092         /**
1093          * @return the flags for this account
1094          * @see #FLAGS_NOTIFY_NEW_MAIL
1095          * @see #FLAGS_VIBRATE_ALWAYS
1096          * @see #FLAGS_VIBRATE_WHEN_SILENT
1097          */
getFlags()1098         public int getFlags() {
1099             return mFlags;
1100         }
1101 
1102         /**
1103          * Set the flags for this account
1104          * @see #FLAGS_NOTIFY_NEW_MAIL
1105          * @see #FLAGS_VIBRATE_ALWAYS
1106          * @see #FLAGS_VIBRATE_WHEN_SILENT
1107          * @param newFlags the new value for the flags
1108          */
setFlags(int newFlags)1109         public void setFlags(int newFlags) {
1110             mFlags = newFlags;
1111         }
1112 
1113         /**
1114          * @return the ringtone Uri for this account
1115          */
getRingtone()1116         public String getRingtone() {
1117             return mRingtoneUri;
1118         }
1119 
1120         /**
1121          * Set the ringtone Uri for this account
1122          * @param newUri the new URI string for the ringtone for this account
1123          */
setRingtone(String newUri)1124         public void setRingtone(String newUri) {
1125             mRingtoneUri = newUri;
1126         }
1127 
1128         /**
1129          * Set the "delete policy" as a simple 0,1,2 value set.
1130          * @param newPolicy the new delete policy
1131          */
setDeletePolicy(int newPolicy)1132         public void setDeletePolicy(int newPolicy) {
1133             mFlags &= ~FLAGS_DELETE_POLICY_MASK;
1134             mFlags |= (newPolicy << FLAGS_DELETE_POLICY_SHIFT) & FLAGS_DELETE_POLICY_MASK;
1135         }
1136 
1137         /**
1138          * Return the "delete policy" as a simple 0,1,2 value set.
1139          * @return the current delete policy
1140          */
getDeletePolicy()1141         public int getDeletePolicy() {
1142             return (mFlags & FLAGS_DELETE_POLICY_MASK) >> FLAGS_DELETE_POLICY_SHIFT;
1143         }
1144 
1145         /**
1146          * Return the Uuid associated with this account.  This is primarily for compatibility
1147          * with accounts set up by previous versions, because there are externals references
1148          * to the Uuid (e.g. desktop shortcuts).
1149          */
getUuid()1150         public String getUuid() {
1151             return mCompatibilityUuid;
1152         }
1153 
1154         /**
1155          * For compatibility while converting to provider model, generate a "store URI"
1156          *
1157          * @return a string in the form of a Uri, as used by the other parts of the email app
1158          */
getStoreUri(Context context)1159         public String getStoreUri(Context context) {
1160             // reconstitute if necessary
1161             if (mHostAuthRecv == null) {
1162                 mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv);
1163             }
1164             // convert if available
1165             if (mHostAuthRecv != null) {
1166                 String storeUri = mHostAuthRecv.getStoreUri();
1167                 if (storeUri != null) {
1168                     return storeUri;
1169                 }
1170             }
1171             return "";
1172         }
1173 
1174         /**
1175          * For compatibility while converting to provider model, generate a "sender URI"
1176          *
1177          * @return a string in the form of a Uri, as used by the other parts of the email app
1178          */
getSenderUri(Context context)1179         public String getSenderUri(Context context) {
1180             // reconstitute if necessary
1181             if (mHostAuthSend == null) {
1182                 mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend);
1183             }
1184             // convert if available
1185             if (mHostAuthSend != null) {
1186                 String senderUri = mHostAuthSend.getStoreUri();
1187                 if (senderUri != null) {
1188                     return senderUri;
1189                 }
1190             }
1191             return "";
1192         }
1193 
1194         /**
1195          * For compatibility while converting to provider model, set the store URI
1196          *
1197          * @param context
1198          * @param storeUri the new value
1199          */
1200         @Deprecated
setStoreUri(Context context, String storeUri)1201         public void setStoreUri(Context context, String storeUri) {
1202             // reconstitute or create if necessary
1203             if (mHostAuthRecv == null) {
1204                 if (mHostAuthKeyRecv != 0) {
1205                     mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv);
1206                 } else {
1207                     mHostAuthRecv = new EmailContent.HostAuth();
1208                 }
1209             }
1210 
1211             if (mHostAuthRecv != null) {
1212                 mHostAuthRecv.setStoreUri(storeUri);
1213             }
1214         }
1215 
1216         /**
1217          * For compatibility while converting to provider model, set the sender URI
1218          *
1219          * @param context
1220          * @param senderUri the new value
1221          */
1222         @Deprecated
setSenderUri(Context context, String senderUri)1223         public void setSenderUri(Context context, String senderUri) {
1224             // reconstitute or create if necessary
1225             if (mHostAuthSend == null) {
1226                 if (mHostAuthKeySend != 0) {
1227                     mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend);
1228                 } else {
1229                     mHostAuthSend = new EmailContent.HostAuth();
1230                 }
1231             }
1232 
1233             if (mHostAuthSend != null) {
1234                 mHostAuthSend.setStoreUri(senderUri);
1235             }
1236         }
1237 
1238         /**
1239          * For compatibility while converting to provider model, generate a "local store URI"
1240          *
1241          * @return a string in the form of a Uri, as used by the other parts of the email app
1242          */
getLocalStoreUri(Context context)1243         public String getLocalStoreUri(Context context) {
1244             return "local://localhost/" + context.getDatabasePath(getUuid() + ".db");
1245         }
1246 
1247         /**
1248          * Set the account to be the default account.  If this is set to "true", when the account
1249          * is saved, all other accounts will have the same value set to "false".
1250          * @param newDefaultState the new default state - if true, others will be cleared.
1251          */
setDefaultAccount(boolean newDefaultState)1252         public void setDefaultAccount(boolean newDefaultState) {
1253             mIsDefault = newDefaultState;
1254         }
1255 
1256         /**
1257          * Helper method for finding the default account.
1258          */
getDefaultAccountWhere(Context context, String where)1259         static private long getDefaultAccountWhere(Context context, String where) {
1260             Cursor cursor = context.getContentResolver().query(CONTENT_URI,
1261                     DEFAULT_ID_PROJECTION,
1262                     where, null, null);
1263             try {
1264                 if (cursor.moveToFirst()) {
1265                     return cursor.getLong(0);   // column 0 is id
1266                 }
1267             } finally {
1268                 cursor.close();
1269             }
1270             return -1;
1271         }
1272 
1273         /**
1274          * @return {@link Uri} to this {@link Account} in the
1275          * {@code content://com.android.email.provider/account/UUID} format, which is safe to use
1276          * for desktop shortcuts.
1277          *
1278          * <p>We don't want to store _id in shortcuts, because
1279          * {@link com.android.email.AccountBackupRestore} won't preserve it.
1280          */
getShortcutSafeUri()1281         public Uri getShortcutSafeUri() {
1282             return getShortcutSafeUriFromUuid(mCompatibilityUuid);
1283         }
1284 
1285         /**
1286          * @return {@link Uri} to an {@link Account} with a {@code uuid}.
1287          */
getShortcutSafeUriFromUuid(String uuid)1288         public static Uri getShortcutSafeUriFromUuid(String uuid) {
1289             return CONTENT_URI.buildUpon().appendEncodedPath(uuid).build();
1290         }
1291 
1292         /**
1293          * Parse {@link Uri} in the {@code content://com.android.email.provider/account/ID} format
1294          * where ID = account id (used on Eclair, Android 2.0-2.1) or UUID, and return _id of
1295          * the {@link Account} associated with it.
1296          *
1297          * @param context context to access DB
1298          * @param uri URI of interest
1299          * @return _id of the {@link Account} associated with ID, or -1 if none found.
1300          */
getAccountIdFromShortcutSafeUri(Context context, Uri uri)1301         public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) {
1302             // Make sure the URI is in the correct format.
1303             if (!"content".equals(uri.getScheme())
1304                     || !EmailContent.AUTHORITY.equals(uri.getAuthority())) {
1305                 return -1;
1306             }
1307 
1308             final List<String> ps = uri.getPathSegments();
1309             if (ps.size() != 2 || !"account".equals(ps.get(0))) {
1310                 return -1;
1311             }
1312 
1313             // Now get the ID part.
1314             final String id = ps.get(1);
1315 
1316             // First, see if ID can be parsed as long.  (Eclair-style)
1317             // (UUIDs have '-' in them, so they are always non-parsable.)
1318             try {
1319                 return Long.parseLong(id);
1320             } catch (NumberFormatException ok) {
1321                 // OK, it's not a long.  Continue...
1322             }
1323 
1324             // Now id is a UUId.
1325             Cursor cursor = context.getContentResolver().query(CONTENT_URI, ID_PROJECTION,
1326                     UUID_SELECTION, new String[] {id}, null);
1327             try {
1328                 if (cursor.moveToFirst()) {
1329                     return cursor.getLong(0);   // column 0 is id
1330                 }
1331             } finally {
1332                 cursor.close();
1333             }
1334             return -1; // Not found.
1335         }
1336 
1337         /**
1338          * Return the id of the default account.  If one hasn't been explicitly specified, return
1339          * the first one in the database.  For any account saved in the DB, this must be used
1340          * to check for the default account - the mIsDefault field is set lazily and may be
1341          * incorrect.
1342          * @param context the caller's context
1343          * @return the id of the default account, or -1 if there are no accounts
1344          */
getDefaultAccountId(Context context)1345         static public long getDefaultAccountId(Context context) {
1346             long id = getDefaultAccountWhere(context, AccountColumns.IS_DEFAULT + "=1");
1347             if (id == -1) {
1348                 id = getDefaultAccountWhere(context, null);
1349             }
1350             return id;
1351         }
1352 
1353         /**
1354          * @return true if an {@code accountId} is assigned to any existing account.
1355          */
isValidId(Context context, long accountId)1356         public static boolean isValidId(Context context, long accountId) {
1357             Cursor cursor = context.getContentResolver().query(CONTENT_URI, ID_PROJECTION,
1358                     ID_SELECTION, new String[] {"" + accountId}, null);
1359             try {
1360                 if (cursor.moveToFirst()) {
1361                     return true;
1362                 }
1363             } finally {
1364                 cursor.close();
1365             }
1366             return false; // Not found.
1367         }
1368 
1369         /**
1370          * Override update to enforce a single default account, and do it atomically
1371          */
1372         @Override
update(Context context, ContentValues cv)1373         public int update(Context context, ContentValues cv) {
1374             if (cv.containsKey(AccountColumns.IS_DEFAULT) &&
1375                     cv.getAsBoolean(AccountColumns.IS_DEFAULT)) {
1376                 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1377                 ContentValues cv1 = new ContentValues();
1378                 cv1.put(AccountColumns.IS_DEFAULT, false);
1379                 // Clear the default flag in all accounts
1380                 ops.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build());
1381                 // Update this account
1382                 ops.add(ContentProviderOperation
1383                         .newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId))
1384                         .withValues(cv).build());
1385                 try {
1386                     context.getContentResolver().applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
1387                     return 1;
1388                 } catch (RemoteException e) {
1389                     // There is nothing to be done here; fail by returning 0
1390                 } catch (OperationApplicationException e) {
1391                     // There is nothing to be done here; fail by returning 0
1392                 }
1393                 return 0;
1394             }
1395             return super.update(context, cv);
1396         }
1397 
1398         /*
1399          * Override this so that we can store the HostAuth's first and link them to the Account
1400          * (non-Javadoc)
1401          * @see com.android.email.provider.EmailContent#save(android.content.Context)
1402          */
1403         @Override
save(Context context)1404         public Uri save(Context context) {
1405             if (isSaved()) {
1406                 throw new UnsupportedOperationException();
1407             }
1408             // This logic is in place so I can (a) short circuit the expensive stuff when
1409             // possible, and (b) override (and throw) if anyone tries to call save() or update()
1410             // directly for Account, which are unsupported.
1411             if (mHostAuthRecv == null && mHostAuthSend == null && mIsDefault == false) {
1412                     return super.save(context);
1413             }
1414 
1415             int index = 0;
1416             int recvIndex = -1;
1417             int sendIndex = -1;
1418 
1419             // Create operations for saving the send and recv hostAuths
1420             // Also, remember which operation in the array they represent
1421             ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1422             if (mHostAuthRecv != null) {
1423                 recvIndex = index++;
1424                 ops.add(ContentProviderOperation.newInsert(mHostAuthRecv.mBaseUri)
1425                         .withValues(mHostAuthRecv.toContentValues())
1426                         .build());
1427             }
1428             if (mHostAuthSend != null) {
1429                 sendIndex = index++;
1430                 ops.add(ContentProviderOperation.newInsert(mHostAuthRecv.mBaseUri)
1431                         .withValues(mHostAuthSend.toContentValues())
1432                         .build());
1433             }
1434 
1435             // Create operations for making this the only default account
1436             // Note, these are always updates because they change existing accounts
1437             if (mIsDefault) {
1438                 index++;
1439                 ContentValues cv1 = new ContentValues();
1440                 cv1.put(AccountColumns.IS_DEFAULT, 0);
1441                 ops.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build());
1442             }
1443 
1444             // Now do the Account
1445             ContentValues cv = null;
1446             if (recvIndex >= 0 || sendIndex >= 0) {
1447                 cv = new ContentValues();
1448                 if (recvIndex >= 0) {
1449                     cv.put(Account.HOST_AUTH_KEY_RECV, recvIndex);
1450                 }
1451                 if (sendIndex >= 0) {
1452                     cv.put(Account.HOST_AUTH_KEY_SEND, sendIndex);
1453                 }
1454             }
1455 
1456             ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(mBaseUri);
1457             b.withValues(toContentValues());
1458             if (cv != null) {
1459                 b.withValueBackReferences(cv);
1460             }
1461             ops.add(b.build());
1462 
1463             try {
1464                 ContentProviderResult[] results =
1465                     context.getContentResolver().applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
1466                 // If saving, set the mId's of the various saved objects
1467                 if (recvIndex >= 0) {
1468                     long newId = getId(results[recvIndex].uri);
1469                     mHostAuthKeyRecv = newId;
1470                     mHostAuthRecv.mId = newId;
1471                 }
1472                 if (sendIndex >= 0) {
1473                     long newId = getId(results[sendIndex].uri);
1474                     mHostAuthKeySend = newId;
1475                     mHostAuthSend.mId = newId;
1476                 }
1477                 Uri u = results[index].uri;
1478                 mId = getId(u);
1479                 return u;
1480             } catch (RemoteException e) {
1481                 // There is nothing to be done here; fail by returning null
1482             } catch (OperationApplicationException e) {
1483                 // There is nothing to be done here; fail by returning null
1484             }
1485             return null;
1486         }
1487 
1488         @Override
toContentValues()1489         public ContentValues toContentValues() {
1490             ContentValues values = new ContentValues();
1491             values.put(AccountColumns.DISPLAY_NAME, mDisplayName);
1492             values.put(AccountColumns.EMAIL_ADDRESS, mEmailAddress);
1493             values.put(AccountColumns.SYNC_KEY, mSyncKey);
1494             values.put(AccountColumns.SYNC_LOOKBACK, mSyncLookback);
1495             values.put(AccountColumns.SYNC_INTERVAL, mSyncInterval);
1496             values.put(AccountColumns.HOST_AUTH_KEY_RECV, mHostAuthKeyRecv);
1497             values.put(AccountColumns.HOST_AUTH_KEY_SEND, mHostAuthKeySend);
1498             values.put(AccountColumns.FLAGS, mFlags);
1499             values.put(AccountColumns.IS_DEFAULT, mIsDefault);
1500             values.put(AccountColumns.COMPATIBILITY_UUID, mCompatibilityUuid);
1501             values.put(AccountColumns.SENDER_NAME, mSenderName);
1502             values.put(AccountColumns.RINGTONE_URI, mRingtoneUri);
1503             values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion);
1504             values.put(AccountColumns.NEW_MESSAGE_COUNT, mNewMessageCount);
1505             values.put(AccountColumns.SECURITY_FLAGS, mSecurityFlags);
1506             values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey);
1507             values.put(AccountColumns.SIGNATURE, mSignature);
1508             return values;
1509         }
1510 
1511         /**
1512          * Supports Parcelable
1513          */
describeContents()1514         public int describeContents() {
1515             return 0;
1516         }
1517 
1518         /**
1519          * Supports Parcelable
1520          */
1521         public static final Parcelable.Creator<EmailContent.Account> CREATOR
1522                 = new Parcelable.Creator<EmailContent.Account>() {
1523             public EmailContent.Account createFromParcel(Parcel in) {
1524                 return new EmailContent.Account(in);
1525             }
1526 
1527             public EmailContent.Account[] newArray(int size) {
1528                 return new EmailContent.Account[size];
1529             }
1530         };
1531 
1532         /**
1533          * Supports Parcelable
1534          */
writeToParcel(Parcel dest, int flags)1535         public void writeToParcel(Parcel dest, int flags) {
1536             // mBaseUri is not parceled
1537             dest.writeLong(mId);
1538             dest.writeString(mDisplayName);
1539             dest.writeString(mEmailAddress);
1540             dest.writeString(mSyncKey);
1541             dest.writeInt(mSyncLookback);
1542             dest.writeInt(mSyncInterval);
1543             dest.writeLong(mHostAuthKeyRecv);
1544             dest.writeLong(mHostAuthKeySend);
1545             dest.writeInt(mFlags);
1546             dest.writeByte(mIsDefault ? (byte)1 : (byte)0);
1547             dest.writeString(mCompatibilityUuid);
1548             dest.writeString(mSenderName);
1549             dest.writeString(mRingtoneUri);
1550             dest.writeString(mProtocolVersion);
1551             dest.writeInt(mNewMessageCount);
1552             dest.writeInt(mSecurityFlags);
1553             dest.writeString(mSecuritySyncKey);
1554             dest.writeString(mSignature);
1555 
1556             if (mHostAuthRecv != null) {
1557                 dest.writeByte((byte)1);
1558                 mHostAuthRecv.writeToParcel(dest, flags);
1559             } else {
1560                 dest.writeByte((byte)0);
1561             }
1562 
1563             if (mHostAuthSend != null) {
1564                 dest.writeByte((byte)1);
1565                 mHostAuthSend.writeToParcel(dest, flags);
1566             } else {
1567                 dest.writeByte((byte)0);
1568             }
1569         }
1570 
1571         /**
1572          * Supports Parcelable
1573          */
Account(Parcel in)1574         public Account(Parcel in) {
1575             mBaseUri = EmailContent.Account.CONTENT_URI;
1576             mId = in.readLong();
1577             mDisplayName = in.readString();
1578             mEmailAddress = in.readString();
1579             mSyncKey = in.readString();
1580             mSyncLookback = in.readInt();
1581             mSyncInterval = in.readInt();
1582             mHostAuthKeyRecv = in.readLong();
1583             mHostAuthKeySend = in.readLong();
1584             mFlags = in.readInt();
1585             mIsDefault = in.readByte() == 1;
1586             mCompatibilityUuid = in.readString();
1587             mSenderName = in.readString();
1588             mRingtoneUri = in.readString();
1589             mProtocolVersion = in.readString();
1590             mNewMessageCount = in.readInt();
1591             mSecurityFlags = in.readInt();
1592             mSecuritySyncKey = in.readString();
1593             mSignature = in.readString();
1594 
1595             mHostAuthRecv = null;
1596             if (in.readByte() == 1) {
1597                 mHostAuthRecv = new EmailContent.HostAuth(in);
1598             }
1599 
1600             mHostAuthSend = null;
1601             if (in.readByte() == 1) {
1602                 mHostAuthSend = new EmailContent.HostAuth(in);
1603             }
1604         }
1605 
1606         /**
1607          * For debugger support only - DO NOT use for code.
1608          */
1609         @Override
toString()1610         public String toString() {
1611             StringBuilder sb = new StringBuilder('[');
1612             if (mHostAuthRecv != null && mHostAuthRecv.mProtocol != null) {
1613                 sb.append(mHostAuthRecv.mProtocol);
1614                 sb.append(':');
1615             }
1616             if (mDisplayName != null)   sb.append(mDisplayName);
1617             sb.append(':');
1618             if (mEmailAddress != null)  sb.append(mEmailAddress);
1619             sb.append(':');
1620             if (mSenderName != null)    sb.append(mSenderName);
1621             sb.append(']');
1622             return sb.toString();
1623         }
1624 
1625     }
1626 
1627     public interface AttachmentColumns {
1628         public static final String ID = "_id";
1629         // The display name of the attachment
1630         public static final String FILENAME = "fileName";
1631         // The mime type of the attachment
1632         public static final String MIME_TYPE = "mimeType";
1633         // The size of the attachment in bytes
1634         public static final String SIZE = "size";
1635         // The (internal) contentId of the attachment (inline attachments will have these)
1636         public static final String CONTENT_ID = "contentId";
1637         // The location of the loaded attachment (probably a file)
1638         public static final String CONTENT_URI = "contentUri";
1639         // A foreign key into the Message table (the message owning this attachment)
1640         public static final String MESSAGE_KEY = "messageKey";
1641         // The location of the attachment on the server side
1642         // For IMAP, this is a part number (e.g. 2.1); for EAS, it's the internal file name
1643         public static final String LOCATION = "location";
1644         // The transfer encoding of the attachment
1645         public static final String ENCODING = "encoding";
1646         // Not currently used
1647         public static final String CONTENT = "content";
1648         // Flags
1649         public static final String FLAGS = "flags";
1650         // Content that is actually contained in the Attachment row
1651         public static final String CONTENT_BYTES = "content_bytes";
1652     }
1653 
1654     public static final class Attachment extends EmailContent implements AttachmentColumns {
1655         public static final String TABLE_NAME = "Attachment";
1656         public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment");
1657         // This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id)
1658         public static final Uri MESSAGE_ID_URI = Uri.parse(
1659                 EmailContent.CONTENT_URI + "/attachment/message");
1660 
1661         public String mFileName;
1662         public String mMimeType;
1663         public long mSize;
1664         public String mContentId;
1665         public String mContentUri;
1666         public long mMessageKey;
1667         public String mLocation;
1668         public String mEncoding;
1669         public String mContent; // Not currently used
1670         public int mFlags;
1671         public byte[] mContentBytes;
1672 
1673         public static final int CONTENT_ID_COLUMN = 0;
1674         public static final int CONTENT_FILENAME_COLUMN = 1;
1675         public static final int CONTENT_MIME_TYPE_COLUMN = 2;
1676         public static final int CONTENT_SIZE_COLUMN = 3;
1677         public static final int CONTENT_CONTENT_ID_COLUMN = 4;
1678         public static final int CONTENT_CONTENT_URI_COLUMN = 5;
1679         public static final int CONTENT_MESSAGE_ID_COLUMN = 6;
1680         public static final int CONTENT_LOCATION_COLUMN = 7;
1681         public static final int CONTENT_ENCODING_COLUMN = 8;
1682         public static final int CONTENT_CONTENT_COLUMN = 9; // Not currently used
1683         public static final int CONTENT_FLAGS_COLUMN = 10;
1684         public static final int CONTENT_CONTENT_BYTES_COLUMN = 11;
1685         public static final String[] CONTENT_PROJECTION = new String[] {
1686             RECORD_ID, AttachmentColumns.FILENAME, AttachmentColumns.MIME_TYPE,
1687             AttachmentColumns.SIZE, AttachmentColumns.CONTENT_ID, AttachmentColumns.CONTENT_URI,
1688             AttachmentColumns.MESSAGE_KEY, AttachmentColumns.LOCATION, AttachmentColumns.ENCODING,
1689             AttachmentColumns.CONTENT, AttachmentColumns.FLAGS, AttachmentColumns.CONTENT_BYTES
1690         };
1691 
1692         // Bits used in mFlags
1693         // Instruct Rfc822Output to 1) not use Content-Disposition and 2) use multipart/alternative
1694         // with this attachment.  This is only valid if there is one and only one attachment and
1695         // that attachment has this flag set
1696         public static final int FLAG_ICS_ALTERNATIVE_PART = 1<<0;
1697 
1698         /**
1699          * no public constructor since this is a utility class
1700          */
Attachment()1701         public Attachment() {
1702             mBaseUri = CONTENT_URI;
1703         }
1704 
1705          /**
1706          * Restore an Attachment from the database, given its unique id
1707          * @param context
1708          * @param id
1709          * @return the instantiated Attachment
1710          */
restoreAttachmentWithId(Context context, long id)1711         public static Attachment restoreAttachmentWithId (Context context, long id) {
1712             Uri u = ContentUris.withAppendedId(Attachment.CONTENT_URI, id);
1713             Cursor c = context.getContentResolver().query(u, Attachment.CONTENT_PROJECTION,
1714                     null, null, null);
1715 
1716             try {
1717                 if (c.moveToFirst()) {
1718                     return getContent(c, Attachment.class);
1719                 } else {
1720                     return null;
1721                 }
1722             } finally {
1723                 c.close();
1724             }
1725         }
1726 
1727         /**
1728          * Restore all the Attachments of a message given its messageId
1729          */
restoreAttachmentsWithMessageId(Context context, long messageId)1730         public static Attachment[] restoreAttachmentsWithMessageId(Context context,
1731                 long messageId) {
1732             Uri uri = ContentUris.withAppendedId(MESSAGE_ID_URI, messageId);
1733             Cursor c = context.getContentResolver().query(uri, CONTENT_PROJECTION,
1734                     null, null, null);
1735             try {
1736                 int count = c.getCount();
1737                 Attachment[] attachments = new Attachment[count];
1738                 for (int i = 0; i < count; ++i) {
1739                     c.moveToNext();
1740                     attachments[i] = new Attachment().restore(c);
1741                 }
1742                 return attachments;
1743             } finally {
1744                 c.close();
1745             }
1746         }
1747 
1748         /**
1749          * Creates a unique file in the external store by appending a hyphen
1750          * and a number to the given filename.
1751          * @param filename
1752          * @return a new File object, or null if one could not be created
1753          */
createUniqueFile(String filename)1754         public static File createUniqueFile(String filename) {
1755             // TODO Handle internal storage, as required
1756             if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
1757                 File directory = Environment.getExternalStorageDirectory();
1758                 File file = new File(directory, filename);
1759                 if (!file.exists()) {
1760                     return file;
1761                 }
1762                 // Get the extension of the file, if any.
1763                 int index = filename.lastIndexOf('.');
1764                 String name = filename;
1765                 String extension = "";
1766                 if (index != -1) {
1767                     name = filename.substring(0, index);
1768                     extension = filename.substring(index);
1769                 }
1770                 for (int i = 2; i < Integer.MAX_VALUE; i++) {
1771                     file = new File(directory, name + '-' + i + extension);
1772                     if (!file.exists()) {
1773                         return file;
1774                     }
1775                 }
1776                 return null;
1777             }
1778             return null;
1779         }
1780 
1781         @Override
1782         @SuppressWarnings("unchecked")
restore(Cursor cursor)1783         public EmailContent.Attachment restore(Cursor cursor) {
1784             mBaseUri = CONTENT_URI;
1785             mId = cursor.getLong(CONTENT_ID_COLUMN);
1786             mFileName= cursor.getString(CONTENT_FILENAME_COLUMN);
1787             mMimeType = cursor.getString(CONTENT_MIME_TYPE_COLUMN);
1788             mSize = cursor.getLong(CONTENT_SIZE_COLUMN);
1789             mContentId = cursor.getString(CONTENT_CONTENT_ID_COLUMN);
1790             mContentUri = cursor.getString(CONTENT_CONTENT_URI_COLUMN);
1791             mMessageKey = cursor.getLong(CONTENT_MESSAGE_ID_COLUMN);
1792             mLocation = cursor.getString(CONTENT_LOCATION_COLUMN);
1793             mEncoding = cursor.getString(CONTENT_ENCODING_COLUMN);
1794             mContent = cursor.getString(CONTENT_CONTENT_COLUMN);
1795             mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
1796             mContentBytes = cursor.getBlob(CONTENT_CONTENT_BYTES_COLUMN);
1797             return this;
1798         }
1799 
1800         @Override
toContentValues()1801         public ContentValues toContentValues() {
1802             ContentValues values = new ContentValues();
1803             values.put(AttachmentColumns.FILENAME, mFileName);
1804             values.put(AttachmentColumns.MIME_TYPE, mMimeType);
1805             values.put(AttachmentColumns.SIZE, mSize);
1806             values.put(AttachmentColumns.CONTENT_ID, mContentId);
1807             values.put(AttachmentColumns.CONTENT_URI, mContentUri);
1808             values.put(AttachmentColumns.MESSAGE_KEY, mMessageKey);
1809             values.put(AttachmentColumns.LOCATION, mLocation);
1810             values.put(AttachmentColumns.ENCODING, mEncoding);
1811             values.put(AttachmentColumns.CONTENT, mContent);
1812             values.put(AttachmentColumns.FLAGS, mFlags);
1813             values.put(AttachmentColumns.CONTENT_BYTES, mContentBytes);
1814             return values;
1815         }
1816 
describeContents()1817         public int describeContents() {
1818              return 0;
1819         }
1820 
writeToParcel(Parcel dest, int flags)1821         public void writeToParcel(Parcel dest, int flags) {
1822             // mBaseUri is not parceled
1823             dest.writeLong(mId);
1824             dest.writeString(mFileName);
1825             dest.writeString(mMimeType);
1826             dest.writeLong(mSize);
1827             dest.writeString(mContentId);
1828             dest.writeString(mContentUri);
1829             dest.writeLong(mMessageKey);
1830             dest.writeString(mLocation);
1831             dest.writeString(mEncoding);
1832             dest.writeString(mContent);
1833             dest.writeInt(mFlags);
1834             if (mContentBytes == null) {
1835                 dest.writeInt(-1);
1836             } else {
1837                 dest.writeInt(mContentBytes.length);
1838                 dest.writeByteArray(mContentBytes);
1839             }
1840         }
1841 
Attachment(Parcel in)1842         public Attachment(Parcel in) {
1843             mBaseUri = EmailContent.Attachment.CONTENT_URI;
1844             mId = in.readLong();
1845             mFileName = in.readString();
1846             mMimeType = in.readString();
1847             mSize = in.readLong();
1848             mContentId = in.readString();
1849             mContentUri = in.readString();
1850             mMessageKey = in.readLong();
1851             mLocation = in.readString();
1852             mEncoding = in.readString();
1853             mContent = in.readString();
1854             mFlags = in.readInt();
1855             final int contentBytesLen = in.readInt();
1856             if (contentBytesLen == -1) {
1857                 mContentBytes = null;
1858             } else {
1859                 mContentBytes = new byte[contentBytesLen];
1860                 in.readByteArray(mContentBytes);
1861             }
1862          }
1863 
1864         public static final Parcelable.Creator<EmailContent.Attachment> CREATOR
1865         = new Parcelable.Creator<EmailContent.Attachment>() {
1866             public EmailContent.Attachment createFromParcel(Parcel in) {
1867                 return new EmailContent.Attachment(in);
1868             }
1869 
1870             public EmailContent.Attachment[] newArray(int size) {
1871                 return new EmailContent.Attachment[size];
1872             }
1873         };
1874 
1875         @Override
toString()1876         public String toString() {
1877             return "[" + mFileName + ", " + mMimeType + ", " + mSize + ", " + mContentId + ", "
1878                     + mContentUri + ", " + mMessageKey + ", " + mLocation + ", " + mEncoding  + ", "
1879                     + mFlags + ", " + mContentBytes + "]";
1880         }
1881     }
1882 
1883     public interface MailboxColumns {
1884         public static final String ID = "_id";
1885         // The display name of this mailbox [INDEX]
1886         static final String DISPLAY_NAME = "displayName";
1887         // The server's identifier for this mailbox
1888         public static final String SERVER_ID = "serverId";
1889         // The server's identifier for the parent of this mailbox (null = top-level)
1890         public static final String PARENT_SERVER_ID = "parentServerId";
1891         // A foreign key to the Account that owns this mailbox
1892         public static final String ACCOUNT_KEY = "accountKey";
1893         // The type (role) of this mailbox
1894         public static final String TYPE = "type";
1895         // The hierarchy separator character
1896         public static final String DELIMITER = "delimiter";
1897         // Server-based sync key or validity marker (e.g. "SyncKey" for EAS, "uidvalidity" for IMAP)
1898         public static final String SYNC_KEY = "syncKey";
1899         // The sync lookback period for this mailbox (or null if using the account default)
1900         public static final String SYNC_LOOKBACK = "syncLookback";
1901         // The sync frequency for this mailbox (or null if using the account default)
1902         public static final String SYNC_INTERVAL = "syncInterval";
1903         // The time of last successful sync completion (millis)
1904         public static final String SYNC_TIME = "syncTime";
1905         // Cached unread count
1906         public static final String UNREAD_COUNT = "unreadCount";
1907         // Visibility of this folder in a list of folders [INDEX]
1908         public static final String FLAG_VISIBLE = "flagVisible";
1909         // Other states, as a bit field, e.g. CHILDREN_VISIBLE, HAS_CHILDREN
1910         public static final String FLAGS = "flags";
1911         // Backward compatible
1912         public static final String VISIBLE_LIMIT = "visibleLimit";
1913         // Sync status (can be used as desired by sync services)
1914         public static final String SYNC_STATUS = "syncStatus";
1915     }
1916 
1917     public static final class Mailbox extends EmailContent implements SyncColumns, MailboxColumns {
1918         public static final String TABLE_NAME = "Mailbox";
1919         public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
1920         public static final Uri ADD_TO_FIELD_URI =
1921             Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
1922 
1923         public String mDisplayName;
1924         public String mServerId;
1925         public String mParentServerId;
1926         public long mAccountKey;
1927         public int mType;
1928         public int mDelimiter;
1929         public String mSyncKey;
1930         public int mSyncLookback;
1931         public int mSyncInterval;
1932         public long mSyncTime;
1933         public int mUnreadCount;
1934         public boolean mFlagVisible = true;
1935         public int mFlags;
1936         public int mVisibleLimit;
1937         public String mSyncStatus;
1938 
1939         public static final int CONTENT_ID_COLUMN = 0;
1940         public static final int CONTENT_DISPLAY_NAME_COLUMN = 1;
1941         public static final int CONTENT_SERVER_ID_COLUMN = 2;
1942         public static final int CONTENT_PARENT_SERVER_ID_COLUMN = 3;
1943         public static final int CONTENT_ACCOUNT_KEY_COLUMN = 4;
1944         public static final int CONTENT_TYPE_COLUMN = 5;
1945         public static final int CONTENT_DELIMITER_COLUMN = 6;
1946         public static final int CONTENT_SYNC_KEY_COLUMN = 7;
1947         public static final int CONTENT_SYNC_LOOKBACK_COLUMN = 8;
1948         public static final int CONTENT_SYNC_INTERVAL_COLUMN = 9;
1949         public static final int CONTENT_SYNC_TIME_COLUMN = 10;
1950         public static final int CONTENT_UNREAD_COUNT_COLUMN = 11;
1951         public static final int CONTENT_FLAG_VISIBLE_COLUMN = 12;
1952         public static final int CONTENT_FLAGS_COLUMN = 13;
1953         public static final int CONTENT_VISIBLE_LIMIT_COLUMN = 14;
1954         public static final int CONTENT_SYNC_STATUS_COLUMN = 15;
1955         public static final String[] CONTENT_PROJECTION = new String[] {
1956             RECORD_ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.SERVER_ID,
1957             MailboxColumns.PARENT_SERVER_ID, MailboxColumns.ACCOUNT_KEY, MailboxColumns.TYPE,
1958             MailboxColumns.DELIMITER, MailboxColumns.SYNC_KEY, MailboxColumns.SYNC_LOOKBACK,
1959             MailboxColumns.SYNC_INTERVAL, MailboxColumns.SYNC_TIME,MailboxColumns.UNREAD_COUNT,
1960             MailboxColumns.FLAG_VISIBLE, MailboxColumns.FLAGS, MailboxColumns.VISIBLE_LIMIT,
1961             MailboxColumns.SYNC_STATUS
1962         };
1963         public static final long NO_MAILBOX = -1;
1964 
1965         // Sentinel values for the mSyncInterval field of both Mailbox records
1966         public static final int CHECK_INTERVAL_NEVER = -1;
1967         public static final int CHECK_INTERVAL_PUSH = -2;
1968         // The following two sentinel values are used by EAS
1969         // Ping indicates that the EAS mailbox is synced based on a "ping" from the server
1970         public static final int CHECK_INTERVAL_PING = -3;
1971         // Push-Hold indicates an EAS push or ping Mailbox shouldn't sync just yet
1972         public static final int CHECK_INTERVAL_PUSH_HOLD = -4;
1973 
1974         private static final String WHERE_TYPE_AND_ACCOUNT_KEY =
1975             MailboxColumns.TYPE + "=? and " + MailboxColumns.ACCOUNT_KEY + "=?";
1976 
1977         // Types of mailboxes.  The list is ordered to match a typical UI presentation, e.g.
1978         // placing the inbox at the top.
1979         // The "main" mailbox for the account, almost always referred to as "Inbox"
1980         // Arrays of "special_mailbox_display_names" and "special_mailbox_icons" are depends on
1981         // types Id of mailboxes.
1982         public static final int TYPE_INBOX = 0;
1983         // Types of mailboxes
1984         // Holds mail (generic)
1985         public static final int TYPE_MAIL = 1;
1986         // Parent-only mailbox; holds no mail
1987         public static final int TYPE_PARENT = 2;
1988         // Holds drafts
1989         public static final int TYPE_DRAFTS = 3;
1990         // The local outbox associated with the Account
1991         public static final int TYPE_OUTBOX = 4;
1992         // Holds sent mail
1993         public static final int TYPE_SENT = 5;
1994         // Holds deleted mail
1995         public static final int TYPE_TRASH = 6;
1996         // Holds junk mail
1997         public static final int TYPE_JUNK = 7;
1998 
1999         // Types after this are used for non-mail mailboxes (as in EAS)
2000         public static final int TYPE_NOT_EMAIL = 0x40;
2001         public static final int TYPE_CALENDAR = 0x41;
2002         public static final int TYPE_CONTACTS = 0x42;
2003         public static final int TYPE_TASKS = 0x43;
2004         public static final int TYPE_EAS_ACCOUNT_MAILBOX = 0x44;
2005 
2006         // Bit field flags
2007         public static final int FLAG_HAS_CHILDREN = 1<<0;
2008         public static final int FLAG_CHILDREN_VISIBLE = 1<<1;
2009         public static final int FLAG_CANT_PUSH = 1<<2;
2010 
2011         // Magic mailbox ID's
2012         // NOTE:  This is a quick solution for merged mailboxes.  I would rather implement this
2013         // with a more generic way of packaging and sharing queries between activities
2014         public static final long QUERY_ALL_INBOXES = -2;
2015         public static final long QUERY_ALL_UNREAD = -3;
2016         public static final long QUERY_ALL_FAVORITES = -4;
2017         public static final long QUERY_ALL_DRAFTS = -5;
2018         public static final long QUERY_ALL_OUTBOX = -6;
2019 
Mailbox()2020         public Mailbox() {
2021             mBaseUri = CONTENT_URI;
2022         }
2023 
2024          /**
2025          * Restore a Mailbox from the database, given its unique id
2026          * @param context
2027          * @param id
2028          * @return the instantiated Mailbox
2029          */
restoreMailboxWithId(Context context, long id)2030         public static Mailbox restoreMailboxWithId(Context context, long id) {
2031             Uri u = ContentUris.withAppendedId(Mailbox.CONTENT_URI, id);
2032             Cursor c = context.getContentResolver().query(u, Mailbox.CONTENT_PROJECTION,
2033                     null, null, null);
2034 
2035             try {
2036                 if (c.moveToFirst()) {
2037                     return EmailContent.getContent(c, Mailbox.class);
2038                 } else {
2039                     return null;
2040                 }
2041             } finally {
2042                 c.close();
2043             }
2044         }
2045 
2046         @Override
2047         @SuppressWarnings("unchecked")
restore(Cursor cursor)2048         public EmailContent.Mailbox restore(Cursor cursor) {
2049             mBaseUri = CONTENT_URI;
2050             mId = cursor.getLong(CONTENT_ID_COLUMN);
2051             mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN);
2052             mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN);
2053             mParentServerId = cursor.getString(CONTENT_PARENT_SERVER_ID_COLUMN);
2054             mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
2055             mType = cursor.getInt(CONTENT_TYPE_COLUMN);
2056             mDelimiter = cursor.getInt(CONTENT_DELIMITER_COLUMN);
2057             mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN);
2058             mSyncLookback = cursor.getInt(CONTENT_SYNC_LOOKBACK_COLUMN);
2059             mSyncInterval = cursor.getInt(CONTENT_SYNC_INTERVAL_COLUMN);
2060             mSyncTime = cursor.getLong(CONTENT_SYNC_TIME_COLUMN);
2061             mUnreadCount = cursor.getInt(CONTENT_UNREAD_COUNT_COLUMN);
2062             mFlagVisible = cursor.getInt(CONTENT_FLAG_VISIBLE_COLUMN) == 1;
2063             mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
2064             mVisibleLimit = cursor.getInt(CONTENT_VISIBLE_LIMIT_COLUMN);
2065             mSyncStatus = cursor.getString(CONTENT_SYNC_STATUS_COLUMN);
2066             return this;
2067         }
2068 
2069         @Override
toContentValues()2070         public ContentValues toContentValues() {
2071             ContentValues values = new ContentValues();
2072             values.put(MailboxColumns.DISPLAY_NAME, mDisplayName);
2073             values.put(MailboxColumns.SERVER_ID, mServerId);
2074             values.put(MailboxColumns.PARENT_SERVER_ID, mParentServerId);
2075             values.put(MailboxColumns.ACCOUNT_KEY, mAccountKey);
2076             values.put(MailboxColumns.TYPE, mType);
2077             values.put(MailboxColumns.DELIMITER, mDelimiter);
2078             values.put(MailboxColumns.SYNC_KEY, mSyncKey);
2079             values.put(MailboxColumns.SYNC_LOOKBACK, mSyncLookback);
2080             values.put(MailboxColumns.SYNC_INTERVAL, mSyncInterval);
2081             values.put(MailboxColumns.SYNC_TIME, mSyncTime);
2082             values.put(MailboxColumns.UNREAD_COUNT, mUnreadCount);
2083             values.put(MailboxColumns.FLAG_VISIBLE, mFlagVisible);
2084             values.put(MailboxColumns.FLAGS, mFlags);
2085             values.put(MailboxColumns.VISIBLE_LIMIT, mVisibleLimit);
2086             values.put(MailboxColumns.SYNC_STATUS, mSyncStatus);
2087             return values;
2088         }
2089 
2090         /**
2091          * Convenience method to return the id of a given type of Mailbox for a given Account
2092          * @param context the caller's context, used to get a ContentResolver
2093          * @param accountId the id of the account to be queried
2094          * @param type the mailbox type, as defined above
2095          * @return the id of the mailbox, or -1 if not found
2096          */
findMailboxOfType(Context context, long accountId, int type)2097         public static long findMailboxOfType(Context context, long accountId, int type) {
2098             long mailboxId = NO_MAILBOX;
2099             String[] bindArguments = new String[] {Long.toString(type), Long.toString(accountId)};
2100             Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI,
2101                     ID_PROJECTION, WHERE_TYPE_AND_ACCOUNT_KEY, bindArguments, null);
2102             try {
2103                 if (c.moveToFirst()) {
2104                     mailboxId = c.getLong(ID_PROJECTION_COLUMN);
2105                 }
2106             } finally {
2107                 c.close();
2108             }
2109             return mailboxId;
2110         }
2111 
2112         /**
2113          * Convenience method that returns the mailbox found using the method above
2114          */
restoreMailboxOfType(Context context, long accountId, int type)2115         public static Mailbox restoreMailboxOfType(Context context, long accountId, int type) {
2116             long mailboxId = findMailboxOfType(context, accountId, type);
2117             if (mailboxId != Mailbox.NO_MAILBOX) {
2118                 return Mailbox.restoreMailboxWithId(context, mailboxId);
2119             }
2120             return null;
2121         }
2122     }
2123 
2124     public interface HostAuthColumns {
2125         public static final String ID = "_id";
2126         // The protocol (e.g. "imap", "pop3", "eas", "smtp"
2127         static final String PROTOCOL = "protocol";
2128         // The host address
2129         static final String ADDRESS = "address";
2130         // The port to use for the connection
2131         static final String PORT = "port";
2132         // General purpose flags
2133         static final String FLAGS = "flags";
2134         // The login (user name)
2135         static final String LOGIN = "login";
2136         // Password
2137         static final String PASSWORD = "password";
2138         // A domain or path, if required (used in IMAP and EAS)
2139         static final String DOMAIN = "domain";
2140         // DEPRECATED - Will not be set or stored
2141         static final String ACCOUNT_KEY = "accountKey";
2142     }
2143 
2144     public static final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable {
2145         public static final String TABLE_NAME = "HostAuth";
2146         public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
2147 
2148         public static final int FLAG_SSL = 1;
2149         public static final int FLAG_TLS = 2;
2150         public static final int FLAG_AUTHENTICATE = 4;
2151         public static final int FLAG_TRUST_ALL_CERTIFICATES = 8;
2152 
2153         public String mProtocol;
2154         public String mAddress;
2155         public int mPort;
2156         public int mFlags;
2157         public String mLogin;
2158         public String mPassword;
2159         public String mDomain;
2160         public long mAccountKey;        // DEPRECATED - Will not be set or stored
2161 
2162         public static final int CONTENT_ID_COLUMN = 0;
2163         public static final int CONTENT_PROTOCOL_COLUMN = 1;
2164         public static final int CONTENT_ADDRESS_COLUMN = 2;
2165         public static final int CONTENT_PORT_COLUMN = 3;
2166         public static final int CONTENT_FLAGS_COLUMN = 4;
2167         public static final int CONTENT_LOGIN_COLUMN = 5;
2168         public static final int CONTENT_PASSWORD_COLUMN = 6;
2169         public static final int CONTENT_DOMAIN_COLUMN = 7;
2170         public static final int CONTENT_ACCOUNT_KEY_COLUMN = 8;
2171 
2172         public static final String[] CONTENT_PROJECTION = new String[] {
2173             RECORD_ID, HostAuthColumns.PROTOCOL, HostAuthColumns.ADDRESS, HostAuthColumns.PORT,
2174             HostAuthColumns.FLAGS, HostAuthColumns.LOGIN,
2175             HostAuthColumns.PASSWORD, HostAuthColumns.DOMAIN,
2176             HostAuthColumns.ACCOUNT_KEY
2177         };
2178 
2179         /**
2180          * no public constructor since this is a utility class
2181          */
HostAuth()2182         public HostAuth() {
2183             mBaseUri = CONTENT_URI;
2184 
2185             // other defaults policy)
2186             mPort = -1;
2187         }
2188 
2189          /**
2190          * Restore a HostAuth from the database, given its unique id
2191          * @param context
2192          * @param id
2193          * @return the instantiated HostAuth
2194          */
restoreHostAuthWithId(Context context, long id)2195         public static HostAuth restoreHostAuthWithId(Context context, long id) {
2196             Uri u = ContentUris.withAppendedId(EmailContent.HostAuth.CONTENT_URI, id);
2197             Cursor c = context.getContentResolver().query(u, HostAuth.CONTENT_PROJECTION,
2198                     null, null, null);
2199 
2200             try {
2201                 if (c.moveToFirst()) {
2202                     return getContent(c, HostAuth.class);
2203                 } else {
2204                     return null;
2205                 }
2206             } finally {
2207                 c.close();
2208             }
2209         }
2210 
2211         @Override
2212         @SuppressWarnings("unchecked")
restore(Cursor cursor)2213         public EmailContent.HostAuth restore(Cursor cursor) {
2214             mBaseUri = CONTENT_URI;
2215             mId = cursor.getLong(CONTENT_ID_COLUMN);
2216             mProtocol = cursor.getString(CONTENT_PROTOCOL_COLUMN);
2217             mAddress = cursor.getString(CONTENT_ADDRESS_COLUMN);
2218             mPort = cursor.getInt(CONTENT_PORT_COLUMN);
2219             mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
2220             mLogin = cursor.getString(CONTENT_LOGIN_COLUMN);
2221             mPassword = cursor.getString(CONTENT_PASSWORD_COLUMN);
2222             mDomain = cursor.getString(CONTENT_DOMAIN_COLUMN);
2223             mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
2224             return this;
2225         }
2226 
2227         @Override
toContentValues()2228         public ContentValues toContentValues() {
2229             ContentValues values = new ContentValues();
2230             values.put(HostAuthColumns.PROTOCOL, mProtocol);
2231             values.put(HostAuthColumns.ADDRESS, mAddress);
2232             values.put(HostAuthColumns.PORT, mPort);
2233             values.put(HostAuthColumns.FLAGS, mFlags);
2234             values.put(HostAuthColumns.LOGIN, mLogin);
2235             values.put(HostAuthColumns.PASSWORD, mPassword);
2236             values.put(HostAuthColumns.DOMAIN, mDomain);
2237             values.put(HostAuthColumns.ACCOUNT_KEY, mAccountKey);
2238             return values;
2239         }
2240 
2241         /**
2242          * For compatibility while converting to provider model, generate a "store URI"
2243          * TODO cache this so we don't rebuild every time
2244          *
2245          * @return a string in the form of a Uri, as used by the other parts of the email app
2246          */
getStoreUri()2247         public String getStoreUri() {
2248             String security;
2249             switch (mFlags & (FLAG_SSL | FLAG_TLS | FLAG_TRUST_ALL_CERTIFICATES)) {
2250                 case FLAG_SSL:
2251                     security = "+ssl+";
2252                     break;
2253                 case FLAG_SSL | FLAG_TRUST_ALL_CERTIFICATES:
2254                     security = "+ssl+trustallcerts";
2255                     break;
2256                 case FLAG_TLS:
2257                     security = "+tls+";
2258                     break;
2259                 case FLAG_TLS | FLAG_TRUST_ALL_CERTIFICATES:
2260                     security = "+tls+trustallcerts";
2261                     break;
2262                 default:
2263                     security = "";
2264                     break;
2265             }
2266             String userInfo = null;
2267             if ((mFlags & FLAG_AUTHENTICATE) != 0) {
2268                 String trimUser = (mLogin != null) ? mLogin.trim() : "";
2269                 String password = (mPassword != null) ? mPassword : "";
2270                 userInfo = trimUser + ":" + password;
2271             }
2272             String address = (mAddress != null) ? mAddress.trim() : null;
2273             String path = (mDomain != null) ? "/" + mDomain : null;
2274 
2275             URI uri;
2276             try {
2277                 uri = new URI(
2278                         mProtocol + security,
2279                         userInfo,
2280                         address,
2281                         mPort,
2282                         path,
2283                         null,
2284                         null);
2285                 return uri.toString();
2286             } catch (URISyntaxException e) {
2287                 return null;
2288             }
2289         }
2290 
2291         /**
2292          * For compatibility while converting to provider model, set fields from a "store URI"
2293          *
2294          * @param uriString a String containing a Uri
2295          */
2296         @Deprecated
setStoreUri(String uriString)2297         public void setStoreUri(String uriString) {
2298             try {
2299                 URI uri = new URI(uriString);
2300                 mLogin = null;
2301                 mPassword = null;
2302                 mFlags &= ~FLAG_AUTHENTICATE;
2303                 if (uri.getUserInfo() != null) {
2304                     String[] userInfoParts = uri.getUserInfo().split(":", 2);
2305                     mLogin = userInfoParts[0];
2306                     mFlags |= FLAG_AUTHENTICATE;
2307                     if (userInfoParts.length > 1) {
2308                         mPassword = userInfoParts[1];
2309                     }
2310                 }
2311 
2312                 // Set protocol, security, and additional flags based on uri scheme
2313                 String[] schemeParts = uri.getScheme().split("\\+");
2314                 mProtocol = (schemeParts.length >= 1) ? schemeParts[0] : null;
2315                 mFlags &= ~(FLAG_SSL | FLAG_TLS | FLAG_TRUST_ALL_CERTIFICATES);
2316                 boolean ssl = false;
2317                 if (schemeParts.length >= 2) {
2318                     String part1 = schemeParts[1];
2319                     if ("ssl".equals(part1)) {
2320                         ssl = true;
2321                         mFlags |= FLAG_SSL;
2322                     } else if ("tls".equals(part1)) {
2323                         mFlags |= FLAG_TLS;
2324                     }
2325                     if (schemeParts.length >= 3) {
2326                         String part2 = schemeParts[2];
2327                         if ("trustallcerts".equals(part2)) {
2328                             mFlags |= FLAG_TRUST_ALL_CERTIFICATES;
2329                         }
2330                     }
2331                 }
2332 
2333                 mAddress = uri.getHost();
2334                 mPort = uri.getPort();
2335                 if (mPort == -1) {
2336                     // infer port# from protocol + security
2337                     // SSL implies a different port - TLS runs in the "regular" port
2338                     // TODO:  This really shouldn't be here - it should have been set up
2339                     // in the account setup screens.
2340                     if ("pop3".equals(mProtocol)) {
2341                         mPort = ssl ? 995 : 110;
2342                     } else if ("imap".equals(mProtocol)) {
2343                         mPort = ssl ? 993 : 143;
2344                     } else if ("eas".equals(mProtocol)) {
2345                         mPort = ssl ? 443 : 80;
2346                     }  else if ("smtp".equals(mProtocol)) {
2347                         mPort = ssl ? 465 : 587;
2348                     }
2349                 }
2350 
2351                 if (uri.getPath() != null && uri.getPath().length() > 0) {
2352                     mDomain = uri.getPath().substring(1);
2353                 }
2354 
2355 
2356             } catch (URISyntaxException use) {
2357                 /*
2358                  * We should always be able to parse our own settings.
2359                  */
2360                 throw new Error(use);
2361             }
2362 
2363         }
2364 
2365         /**
2366          * Supports Parcelable
2367          */
describeContents()2368         public int describeContents() {
2369             return 0;
2370         }
2371 
2372         /**
2373          * Supports Parcelable
2374          */
2375         public static final Parcelable.Creator<EmailContent.HostAuth> CREATOR
2376                 = new Parcelable.Creator<EmailContent.HostAuth>() {
2377             public EmailContent.HostAuth createFromParcel(Parcel in) {
2378                 return new EmailContent.HostAuth(in);
2379             }
2380 
2381             public EmailContent.HostAuth[] newArray(int size) {
2382                 return new EmailContent.HostAuth[size];
2383             }
2384         };
2385 
2386         /**
2387          * Supports Parcelable
2388          */
writeToParcel(Parcel dest, int flags)2389         public void writeToParcel(Parcel dest, int flags) {
2390             // mBaseUri is not parceled
2391             dest.writeLong(mId);
2392             dest.writeString(mProtocol);
2393             dest.writeString(mAddress);
2394             dest.writeInt(mPort);
2395             dest.writeInt(mFlags);
2396             dest.writeString(mLogin);
2397             dest.writeString(mPassword);
2398             dest.writeString(mDomain);
2399             dest.writeLong(mAccountKey);
2400         }
2401 
2402         /**
2403          * Supports Parcelable
2404          */
HostAuth(Parcel in)2405         public HostAuth(Parcel in) {
2406             mBaseUri = CONTENT_URI;
2407             mId = in.readLong();
2408             mProtocol = in.readString();
2409             mAddress = in.readString();
2410             mPort = in.readInt();
2411             mFlags = in.readInt();
2412             mLogin = in.readString();
2413             mPassword = in.readString();
2414             mDomain = in.readString();
2415             mAccountKey = in.readLong();
2416         }
2417 
2418         /**
2419          * For debugger support only - DO NOT use for code.
2420          */
2421         @Override
toString()2422         public String toString() {
2423             return getStoreUri();
2424         }
2425     }
2426 }
2427