LegacyConversions.java revision aeee10e57ef4d931e7708fde218d590453a82aea
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 17package com.android.email; 18 19import com.android.emailcommon.Logging; 20import com.android.emailcommon.internet.MimeBodyPart; 21import com.android.emailcommon.internet.MimeHeader; 22import com.android.emailcommon.internet.MimeMessage; 23import com.android.emailcommon.internet.MimeMultipart; 24import com.android.emailcommon.internet.MimeUtility; 25import com.android.emailcommon.internet.TextBody; 26import com.android.emailcommon.mail.Address; 27import com.android.emailcommon.mail.Flag; 28import com.android.emailcommon.mail.Message; 29import com.android.emailcommon.mail.Message.RecipientType; 30import com.android.emailcommon.mail.MessagingException; 31import com.android.emailcommon.mail.Part; 32import com.android.emailcommon.provider.EmailContent; 33import com.android.emailcommon.provider.EmailContent.Attachment; 34import com.android.emailcommon.provider.EmailContent.AttachmentColumns; 35import com.android.emailcommon.provider.EmailContent.HostAuth; 36import com.android.emailcommon.provider.EmailContent.Mailbox; 37import com.android.emailcommon.utility.AttachmentUtilities; 38import com.android.emailcommon.utility.Utility; 39 40import org.apache.commons.io.IOUtils; 41 42import android.content.ContentUris; 43import android.content.ContentValues; 44import android.content.Context; 45import android.database.Cursor; 46import android.net.Uri; 47import android.util.Log; 48 49import java.io.File; 50import java.io.FileOutputStream; 51import java.io.IOException; 52import java.io.InputStream; 53import java.net.URISyntaxException; 54import java.util.ArrayList; 55import java.util.Date; 56import java.util.HashMap; 57 58public class LegacyConversions { 59 60 /** DO NOT CHECK IN "TRUE" */ 61 private static final boolean DEBUG_ATTACHMENTS = false; 62 63 /** Used for mapping folder names to type codes (e.g. inbox, drafts, trash) */ 64 private static final HashMap<String, Integer> 65 sServerMailboxNames = new HashMap<String, Integer>(); 66 67 /** 68 * Values for HEADER_ANDROID_BODY_QUOTED_PART to tag body parts 69 */ 70 /* package */ static final String BODY_QUOTED_PART_REPLY = "quoted-reply"; 71 /* package */ static final String BODY_QUOTED_PART_FORWARD = "quoted-forward"; 72 /* package */ static final String BODY_QUOTED_PART_INTRO = "quoted-intro"; 73 74 /** 75 * Copy field-by-field from a "store" message to a "provider" message 76 * @param message The message we've just downloaded (must be a MimeMessage) 77 * @param localMessage The message we'd like to write into the DB 78 * @result true if dirty (changes were made) 79 */ 80 public static boolean updateMessageFields(EmailContent.Message localMessage, Message message, 81 long accountId, long mailboxId) throws MessagingException { 82 83 Address[] from = message.getFrom(); 84 Address[] to = message.getRecipients(Message.RecipientType.TO); 85 Address[] cc = message.getRecipients(Message.RecipientType.CC); 86 Address[] bcc = message.getRecipients(Message.RecipientType.BCC); 87 Address[] replyTo = message.getReplyTo(); 88 String subject = message.getSubject(); 89 Date sentDate = message.getSentDate(); 90 Date internalDate = message.getInternalDate(); 91 92 if (from != null && from.length > 0) { 93 localMessage.mDisplayName = from[0].toFriendly(); 94 } 95 if (sentDate != null) { 96 localMessage.mTimeStamp = sentDate.getTime(); 97 } 98 if (subject != null) { 99 localMessage.mSubject = subject; 100 } 101 localMessage.mFlagRead = message.isSet(Flag.SEEN); 102 103 // Keep the message in the "unloaded" state until it has (at least) a display name. 104 // This prevents early flickering of empty messages in POP download. 105 if (localMessage.mFlagLoaded != EmailContent.Message.FLAG_LOADED_COMPLETE) { 106 if (localMessage.mDisplayName == null || "".equals(localMessage.mDisplayName)) { 107 localMessage.mFlagLoaded = EmailContent.Message.FLAG_LOADED_UNLOADED; 108 } else { 109 localMessage.mFlagLoaded = EmailContent.Message.FLAG_LOADED_PARTIAL; 110 } 111 } 112 localMessage.mFlagFavorite = message.isSet(Flag.FLAGGED); 113// public boolean mFlagAttachment = false; 114// public int mFlags = 0; 115 116 localMessage.mServerId = message.getUid(); 117 if (internalDate != null) { 118 localMessage.mServerTimeStamp = internalDate.getTime(); 119 } 120// public String mClientId; 121 122 // Only replace the local message-id if a new one was found. This is seen in some ISP's 123 // which may deliver messages w/o a message-id header. 124 String messageId = ((MimeMessage)message).getMessageId(); 125 if (messageId != null) { 126 localMessage.mMessageId = messageId; 127 } 128 129// public long mBodyKey; 130 localMessage.mMailboxKey = mailboxId; 131 localMessage.mAccountKey = accountId; 132 133 if (from != null && from.length > 0) { 134 localMessage.mFrom = Address.pack(from); 135 } 136 137 localMessage.mTo = Address.pack(to); 138 localMessage.mCc = Address.pack(cc); 139 localMessage.mBcc = Address.pack(bcc); 140 localMessage.mReplyTo = Address.pack(replyTo); 141 142// public String mText; 143// public String mHtml; 144// public String mTextReply; 145// public String mHtmlReply; 146 147// // Can be used while building messages, but is NOT saved by the Provider 148// transient public ArrayList<Attachment> mAttachments = null; 149 150 return true; 151 } 152 153 /** 154 * Copy attachments from MimeMessage to provider Message. 155 * 156 * @param context a context for file operations 157 * @param localMessage the attachments will be built against this message 158 * @param attachments the attachments to add 159 * @throws IOException 160 */ 161 public static void updateAttachments(Context context, EmailContent.Message localMessage, 162 ArrayList<Part> attachments) throws MessagingException, IOException { 163 localMessage.mAttachments = null; 164 for (Part attachmentPart : attachments) { 165 addOneAttachment(context, localMessage, attachmentPart); 166 } 167 } 168 169 /** 170 * Add a single attachment part to the message 171 * 172 * This will skip adding attachments if they are already found in the attachments table. 173 * The heuristic for this will fail (false-positive) if two identical attachments are 174 * included in a single POP3 message. 175 * TODO: Fix that, by (elsewhere) simulating an mLocation value based on the attachments 176 * position within the list of multipart/mixed elements. This would make every POP3 attachment 177 * unique, and might also simplify the code (since we could just look at the positions, and 178 * ignore the filename, etc.) 179 * 180 * TODO: Take a closer look at encoding and deal with it if necessary. 181 * 182 * @param context a context for file operations 183 * @param localMessage the attachments will be built against this message 184 * @param part a single attachment part from POP or IMAP 185 * @throws IOException 186 */ 187 private static void addOneAttachment(Context context, EmailContent.Message localMessage, 188 Part part) throws MessagingException, IOException { 189 190 Attachment localAttachment = new Attachment(); 191 192 // Transfer fields from mime format to provider format 193 String contentType = MimeUtility.unfoldAndDecode(part.getContentType()); 194 String name = MimeUtility.getHeaderParameter(contentType, "name"); 195 if (name == null) { 196 String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition()); 197 name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); 198 } 199 200 // Incoming attachment: Try to pull size from disposition (if not downloaded yet) 201 long size = 0; 202 String disposition = part.getDisposition(); 203 if (disposition != null) { 204 String s = MimeUtility.getHeaderParameter(disposition, "size"); 205 if (s != null) { 206 size = Long.parseLong(s); 207 } 208 } 209 210 // Get partId for unloaded IMAP attachments (if any) 211 // This is only provided (and used) when we have structure but not the actual attachment 212 String[] partIds = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA); 213 String partId = partIds != null ? partIds[0] : null; 214 215 localAttachment.mFileName = name; 216 localAttachment.mMimeType = part.getMimeType(); 217 localAttachment.mSize = size; // May be reset below if file handled 218 localAttachment.mContentId = part.getContentId(); 219 localAttachment.mContentUri = null; // Will be rewritten by saveAttachmentBody 220 localAttachment.mMessageKey = localMessage.mId; 221 localAttachment.mLocation = partId; 222 localAttachment.mEncoding = "B"; // TODO - convert other known encodings 223 localAttachment.mAccountKey = localMessage.mAccountKey; 224 225 if (DEBUG_ATTACHMENTS) { 226 Log.d(Logging.LOG_TAG, "Add attachment " + localAttachment); 227 } 228 229 // To prevent duplication - do we already have a matching attachment? 230 // The fields we'll check for equality are: 231 // mFileName, mMimeType, mContentId, mMessageKey, mLocation 232 // NOTE: This will false-positive if you attach the exact same file, twice, to a POP3 233 // message. We can live with that - you'll get one of the copies. 234 Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId); 235 Cursor cursor = context.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION, 236 null, null, null); 237 boolean attachmentFoundInDb = false; 238 try { 239 while (cursor.moveToNext()) { 240 Attachment dbAttachment = new Attachment(); 241 dbAttachment.restore(cursor); 242 // We test each of the fields here (instead of in SQL) because they may be 243 // null, or may be strings. 244 if (stringNotEqual(dbAttachment.mFileName, localAttachment.mFileName)) continue; 245 if (stringNotEqual(dbAttachment.mMimeType, localAttachment.mMimeType)) continue; 246 if (stringNotEqual(dbAttachment.mContentId, localAttachment.mContentId)) continue; 247 if (stringNotEqual(dbAttachment.mLocation, localAttachment.mLocation)) continue; 248 // We found a match, so use the existing attachment id, and stop looking/looping 249 attachmentFoundInDb = true; 250 localAttachment.mId = dbAttachment.mId; 251 if (DEBUG_ATTACHMENTS) { 252 Log.d(Logging.LOG_TAG, "Skipped, found db attachment " + dbAttachment); 253 } 254 break; 255 } 256 } finally { 257 cursor.close(); 258 } 259 260 // Save the attachment (so far) in order to obtain an id 261 if (!attachmentFoundInDb) { 262 localAttachment.save(context); 263 } 264 265 // If an attachment body was actually provided, we need to write the file now 266 saveAttachmentBody(context, part, localAttachment, localMessage.mAccountKey); 267 268 if (localMessage.mAttachments == null) { 269 localMessage.mAttachments = new ArrayList<Attachment>(); 270 } 271 localMessage.mAttachments.add(localAttachment); 272 localMessage.mFlagAttachment = true; 273 } 274 275 /** 276 * Helper for addOneAttachment that compares two strings, deals with nulls, and treats 277 * nulls and empty strings as equal. 278 */ 279 /* package */ static boolean stringNotEqual(String a, String b) { 280 if (a == null && b == null) return false; // fast exit for two null strings 281 if (a == null) a = ""; 282 if (b == null) b = ""; 283 return !a.equals(b); 284 } 285 286 /** 287 * Save the body part of a single attachment, to a file in the attachments directory. 288 */ 289 public static void saveAttachmentBody(Context context, Part part, Attachment localAttachment, 290 long accountId) throws MessagingException, IOException { 291 if (part.getBody() != null) { 292 long attachmentId = localAttachment.mId; 293 294 InputStream in = part.getBody().getInputStream(); 295 296 File saveIn = AttachmentUtilities.getAttachmentDirectory(context, accountId); 297 if (!saveIn.exists()) { 298 saveIn.mkdirs(); 299 } 300 File saveAs = AttachmentUtilities.getAttachmentFilename(context, accountId, 301 attachmentId); 302 saveAs.createNewFile(); 303 FileOutputStream out = new FileOutputStream(saveAs); 304 long copySize = IOUtils.copy(in, out); 305 in.close(); 306 out.close(); 307 308 // update the attachment with the extra information we now know 309 String contentUriString = AttachmentUtilities.getAttachmentUri( 310 accountId, attachmentId).toString(); 311 312 localAttachment.mSize = copySize; 313 localAttachment.mContentUri = contentUriString; 314 315 // update the attachment in the database as well 316 ContentValues cv = new ContentValues(); 317 cv.put(AttachmentColumns.SIZE, copySize); 318 cv.put(AttachmentColumns.CONTENT_URI, contentUriString); 319 Uri uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, attachmentId); 320 context.getContentResolver().update(uri, cv, null, null); 321 } 322 } 323 324 /** 325 * Read a complete Provider message into a legacy message (for IMAP upload). This 326 * is basically the equivalent of LocalFolder.getMessages() + LocalFolder.fetch(). 327 */ 328 public static Message makeMessage(Context context, EmailContent.Message localMessage) 329 throws MessagingException { 330 MimeMessage message = new MimeMessage(); 331 332 // LocalFolder.getMessages() equivalent: Copy message fields 333 message.setSubject(localMessage.mSubject == null ? "" : localMessage.mSubject); 334 Address[] from = Address.unpack(localMessage.mFrom); 335 if (from.length > 0) { 336 message.setFrom(from[0]); 337 } 338 message.setSentDate(new Date(localMessage.mTimeStamp)); 339 message.setUid(localMessage.mServerId); 340 message.setFlag(Flag.DELETED, 341 localMessage.mFlagLoaded == EmailContent.Message.FLAG_LOADED_DELETED); 342 message.setFlag(Flag.SEEN, localMessage.mFlagRead); 343 message.setFlag(Flag.FLAGGED, localMessage.mFlagFavorite); 344// message.setFlag(Flag.DRAFT, localMessage.mMailboxKey == draftMailboxKey); 345 message.setRecipients(RecipientType.TO, Address.unpack(localMessage.mTo)); 346 message.setRecipients(RecipientType.CC, Address.unpack(localMessage.mCc)); 347 message.setRecipients(RecipientType.BCC, Address.unpack(localMessage.mBcc)); 348 message.setReplyTo(Address.unpack(localMessage.mReplyTo)); 349 message.setInternalDate(new Date(localMessage.mServerTimeStamp)); 350 message.setMessageId(localMessage.mMessageId); 351 352 // LocalFolder.fetch() equivalent: build body parts 353 message.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "multipart/mixed"); 354 MimeMultipart mp = new MimeMultipart(); 355 mp.setSubType("mixed"); 356 message.setBody(mp); 357 358 try { 359 addTextBodyPart(mp, "text/html", null, 360 EmailContent.Body.restoreBodyHtmlWithMessageId(context, localMessage.mId)); 361 } catch (RuntimeException rte) { 362 Log.d(Logging.LOG_TAG, "Exception while reading html body " + rte.toString()); 363 } 364 365 try { 366 addTextBodyPart(mp, "text/plain", null, 367 EmailContent.Body.restoreBodyTextWithMessageId(context, localMessage.mId)); 368 } catch (RuntimeException rte) { 369 Log.d(Logging.LOG_TAG, "Exception while reading text body " + rte.toString()); 370 } 371 372 boolean isReply = (localMessage.mFlags & EmailContent.Message.FLAG_TYPE_REPLY) != 0; 373 boolean isForward = (localMessage.mFlags & EmailContent.Message.FLAG_TYPE_FORWARD) != 0; 374 375 // If there is a quoted part (forwarding or reply), add the intro first, and then the 376 // rest of it. If it is opened in some other viewer, it will (hopefully) be displayed in 377 // the same order as we've just set up the blocks: composed text, intro, replied text 378 if (isReply || isForward) { 379 try { 380 addTextBodyPart(mp, "text/plain", BODY_QUOTED_PART_INTRO, 381 EmailContent.Body.restoreIntroTextWithMessageId(context, localMessage.mId)); 382 } catch (RuntimeException rte) { 383 Log.d(Logging.LOG_TAG, "Exception while reading text reply " + rte.toString()); 384 } 385 386 String replyTag = isReply ? BODY_QUOTED_PART_REPLY : BODY_QUOTED_PART_FORWARD; 387 try { 388 addTextBodyPart(mp, "text/html", replyTag, 389 EmailContent.Body.restoreReplyHtmlWithMessageId(context, localMessage.mId)); 390 } catch (RuntimeException rte) { 391 Log.d(Logging.LOG_TAG, "Exception while reading html reply " + rte.toString()); 392 } 393 394 try { 395 addTextBodyPart(mp, "text/plain", replyTag, 396 EmailContent.Body.restoreReplyTextWithMessageId(context, localMessage.mId)); 397 } catch (RuntimeException rte) { 398 Log.d(Logging.LOG_TAG, "Exception while reading text reply " + rte.toString()); 399 } 400 } 401 402 // Attachments 403 // TODO: Make sure we deal with these as structures and don't accidentally upload files 404// Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId); 405// Cursor attachments = context.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION, 406// null, null, null); 407// try { 408// 409// } finally { 410// attachments.close(); 411// } 412 413 return message; 414 } 415 416 /** 417 * Helper method to add a body part for a given type of text, if found 418 * 419 * @param mp The text body part will be added to this multipart 420 * @param contentType The content-type of the text being added 421 * @param quotedPartTag If non-null, HEADER_ANDROID_BODY_QUOTED_PART will be set to this value 422 * @param partText The text to add. If null, nothing happens 423 */ 424 private static void addTextBodyPart(MimeMultipart mp, String contentType, String quotedPartTag, 425 String partText) throws MessagingException { 426 if (partText == null) { 427 return; 428 } 429 TextBody body = new TextBody(partText); 430 MimeBodyPart bp = new MimeBodyPart(body, contentType); 431 if (quotedPartTag != null) { 432 bp.addHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART, quotedPartTag); 433 } 434 mp.addBodyPart(bp); 435 } 436 437 /** 438 * Conversion from provider account to legacy account 439 * 440 * Used for backup/restore. 441 * 442 * @param context application context 443 * @param fromAccount the provider account to be backed up (including transient hostauth's) 444 * @return a legacy Account object ready to be committed to preferences 445 */ 446 /* package */ static Account makeLegacyAccount(Context context, 447 EmailContent.Account fromAccount) { 448 Account result = new Account(context); 449 450 result.setDescription(fromAccount.getDisplayName()); 451 result.setEmail(fromAccount.getEmailAddress()); 452 // fromAccount.mSyncKey - assume lost if restoring 453 result.setSyncWindow(fromAccount.getSyncLookback()); 454 result.setAutomaticCheckIntervalMinutes(fromAccount.getSyncInterval()); 455 // fromAccount.mHostAuthKeyRecv - id not saved; will be reassigned when restoring 456 // fromAccount.mHostAuthKeySend - id not saved; will be reassigned when restoring 457 458 // Provider Account flags, and how they are mapped. 459 // FLAGS_NOTIFY_NEW_MAIL -> mNotifyNewMail 460 // FLAGS_VIBRATE_ALWAYS -> mVibrate 461 // FLAGS_VIBRATE_WHEN_SILENT -> mVibrateWhenSilent 462 // DELETE_POLICY_NEVER -> mDeletePolicy 463 // DELETE_POLICY_7DAYS 464 // DELETE_POLICY_ON_DELETE 465 result.setNotifyNewMail(0 != 466 (fromAccount.getFlags() & EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL)); 467 result.setVibrate(0 != 468 (fromAccount.getFlags() & EmailContent.Account.FLAGS_VIBRATE_ALWAYS)); 469 result.setVibrateWhenSilent(0 != 470 (fromAccount.getFlags() & EmailContent.Account.FLAGS_VIBRATE_WHEN_SILENT)); 471 result.setDeletePolicy(fromAccount.getDeletePolicy()); 472 473 result.mUuid = fromAccount.getUuid(); 474 result.setName(fromAccount.mSenderName); 475 result.setRingtone(fromAccount.mRingtoneUri); 476 result.mProtocolVersion = fromAccount.mProtocolVersion; 477 // int fromAccount.mNewMessageCount = will be reset on next sync 478 result.mSignature = fromAccount.mSignature; 479 480 // Use the existing conversions from HostAuth <-> Uri 481 result.setStoreUri(fromAccount.getStoreUri(context)); 482 result.setSenderUri(fromAccount.getSenderUri(context)); 483 484 return result; 485 } 486 487 /** 488 * Conversion from legacy account to provider account 489 * 490 * Used for backup/restore. 491 * 492 * @param context application context 493 * @param fromAccount the legacy account to convert to modern format 494 * @return an Account ready to be committed to provider 495 */ 496 public static EmailContent.Account makeAccount(Context context, Account fromAccount) { 497 498 EmailContent.Account result = new EmailContent.Account(); 499 500 result.setDisplayName(fromAccount.getDescription()); 501 result.setEmailAddress(fromAccount.getEmail()); 502 result.mSyncKey = null; 503 result.setSyncLookback(fromAccount.getSyncWindow()); 504 result.setSyncInterval(fromAccount.getAutomaticCheckIntervalMinutes()); 505 // result.mHostAuthKeyRecv; -- will be set when object is saved 506 // result.mHostAuthKeySend; -- will be set when object is saved 507 int flags = 0; 508 if (fromAccount.isNotifyNewMail()) flags |= EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL; 509 if (fromAccount.isVibrate()) flags |= EmailContent.Account.FLAGS_VIBRATE_ALWAYS; 510 if (fromAccount.isVibrateWhenSilent()) 511 flags |= EmailContent.Account.FLAGS_VIBRATE_WHEN_SILENT; 512 result.setFlags(flags); 513 result.setDeletePolicy(fromAccount.getDeletePolicy()); 514 // result.setDefaultAccount(); -- will be set by caller, if needed 515 result.mCompatibilityUuid = fromAccount.getUuid(); 516 result.setSenderName(fromAccount.getName()); 517 result.setRingtone(fromAccount.getRingtone()); 518 result.mProtocolVersion = fromAccount.mProtocolVersion; 519 result.mNewMessageCount = 0; 520 result.mSecuritySyncKey = null; 521 result.mPolicyKey = 0; 522 result.mSignature = fromAccount.mSignature; 523 try { 524 HostAuth recvAuth = result.getOrCreateHostAuthRecv(context); 525 Utility.setHostAuthFromString(recvAuth, fromAccount.getStoreUri()); 526 } catch (URISyntaxException e) { 527 result.mHostAuthRecv = new HostAuth(); 528 Log.w(Logging.LOG_TAG, e); 529 } 530 try { 531 HostAuth sendAuth = result.getOrCreateHostAuthSend(context); 532 Utility.setHostAuthFromString(sendAuth, fromAccount.getSenderUri()); 533 } catch (URISyntaxException e) { 534 result.mHostAuthSend = new HostAuth(); 535 Log.w(Logging.LOG_TAG, e); 536 } 537 538 return result; 539 } 540 541 /** 542 * Infer mailbox type from mailbox name. Used by MessagingController (for live folder sync). 543 */ 544 public static synchronized int inferMailboxTypeFromName(Context context, String mailboxName) { 545 if (sServerMailboxNames.size() == 0) { 546 // preload the hashmap, one time only 547 sServerMailboxNames.put( 548 context.getString(R.string.mailbox_name_server_inbox).toLowerCase(), 549 Mailbox.TYPE_INBOX); 550 sServerMailboxNames.put( 551 context.getString(R.string.mailbox_name_server_outbox).toLowerCase(), 552 Mailbox.TYPE_OUTBOX); 553 sServerMailboxNames.put( 554 context.getString(R.string.mailbox_name_server_drafts).toLowerCase(), 555 Mailbox.TYPE_DRAFTS); 556 sServerMailboxNames.put( 557 context.getString(R.string.mailbox_name_server_trash).toLowerCase(), 558 Mailbox.TYPE_TRASH); 559 sServerMailboxNames.put( 560 context.getString(R.string.mailbox_name_server_sent).toLowerCase(), 561 Mailbox.TYPE_SENT); 562 sServerMailboxNames.put( 563 context.getString(R.string.mailbox_name_server_junk).toLowerCase(), 564 Mailbox.TYPE_JUNK); 565 } 566 if (mailboxName == null || mailboxName.length() == 0) { 567 return EmailContent.Mailbox.TYPE_MAIL; 568 } 569 String lowerCaseName = mailboxName.toLowerCase(); 570 Integer type = sServerMailboxNames.get(lowerCaseName); 571 if (type != null) { 572 return type; 573 } 574 return EmailContent.Mailbox.TYPE_MAIL; 575 } 576} 577