Remove all compatibility with pre-2.0 data
* Remove LocalStore (pre-2.0 mail database) and its unit test * Remove UpgradeAccounts (converted pre-2.0 accounts to Provider) * Remove FolderMessageList (receiver for pre-1.6 desktop shortcuts) * Remove "upgrading" paths through LegacyConversions * Clip out dangling references to everything above Bug: 3292310 Change-Id: I5654d55a0879b00b05b63055b94651855a8ee3ef
This commit is contained in:
parent
5e39f90e9d
commit
7d51b7a05b
|
@ -103,13 +103,6 @@
|
|||
android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.UpgradeAccounts"
|
||||
android:label="@string/upgrade_accounts_title"
|
||||
android:theme="@android:style/Theme.NoTitleBar"
|
||||
android:configChanges="keyboardHidden|orientation"
|
||||
>
|
||||
</activity>
|
||||
<!-- Must be exported in order for the AccountManager to launch it -->
|
||||
<!-- Also available for continuous test systems to force account creation -->
|
||||
<activity
|
||||
|
@ -212,25 +205,6 @@
|
|||
android:name=".activity.MessageListXL"
|
||||
>
|
||||
</activity>
|
||||
|
||||
<!--
|
||||
This activity catches shortcuts to account created on Android 1.6 and before,
|
||||
and redirects to MessageList.
|
||||
singleTask is necessary to make sure the activity is really launched.
|
||||
Without it, the framework brings up the app to front, but doesn't necessarily
|
||||
launch the activity.
|
||||
-->
|
||||
<activity
|
||||
android:name=".activity.FolderMessageList"
|
||||
android:launchMode="singleTask"
|
||||
>
|
||||
<intent-filter>
|
||||
<!-- This action is only to allow an entry point for launcher shortcuts -->
|
||||
<action
|
||||
android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MessageView"
|
||||
>
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2010 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/title_bar_medium">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/upgrade_accounts_title"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:shadowColor="?android:attr/colorBackground"
|
||||
android:shadowRadius="2" />
|
||||
</LinearLayout>
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0px"
|
||||
android:layout_weight="1"
|
||||
android:paddingTop="10dip"
|
||||
android:paddingBottom="10dip">
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:drawSelectorOnTop="false"
|
||||
android:fastScrollEnabled="true" />
|
||||
</FrameLayout>
|
||||
<LinearLayout style="@android:style/ButtonBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<View
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1" />
|
||||
<Button android:id="@+id/action_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/okay_action" />
|
||||
<View
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -1,52 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2010, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
android:paddingRight="6dip"
|
||||
android:paddingLeft="6dip"
|
||||
android:gravity="fill" >
|
||||
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_marginBottom="2dip" />
|
||||
|
||||
<ProgressBar android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="100" />
|
||||
|
||||
<TextView android:id="@+id/error"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_marginBottom="2dip" />
|
||||
|
||||
</LinearLayout>
|
|
@ -36,6 +36,10 @@
|
|||
<string name="general_preference_background_attachments_label"></string>
|
||||
<!-- Do Not Translate. Unused string. -->
|
||||
<string name="general_preference_background_attachments_summary"></string>
|
||||
<!-- Do Not Translate. Unused string. -->
|
||||
<string name="upgrade_accounts_title"></string>
|
||||
<!-- Do Not Translate. Unused string. -->
|
||||
<string name="upgrade_accounts_error"></string>
|
||||
|
||||
<!-- Name of application on Home screen -->
|
||||
<string name="app_name">Email</string>
|
||||
|
@ -849,11 +853,6 @@ save attachment.</string>
|
|||
<!-- Title of activity/dialog containing shortcut picker (list of accounts) [CHAR_LIMIT=20] -->
|
||||
<string name="account_shortcut_picker_title">Select an account</string>
|
||||
|
||||
<!-- Title of Upgrade Accounts activity -->
|
||||
<string name="upgrade_accounts_title">Upgrade accounts</string>
|
||||
<!-- Error shown when Upgrade Accounts fails (shown below name of that account) -->
|
||||
<string name="upgrade_accounts_error">Unable to upgrade account</string>
|
||||
|
||||
<!-- Message that appears when adding a Windows Live Hotmail Plus account -->
|
||||
<string name="provider_note_live">Only some \"Plus\" accounts include POP access
|
||||
allowing this program to connect. If you are not able to sign in with
|
||||
|
|
|
@ -17,20 +17,17 @@
|
|||
package com.android.email;
|
||||
|
||||
import com.android.email.mail.Address;
|
||||
import com.android.email.mail.Body;
|
||||
import com.android.email.mail.Flag;
|
||||
import com.android.email.mail.Folder;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.Message.RecipientType;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Part;
|
||||
import com.android.email.mail.Message.RecipientType;
|
||||
import com.android.email.mail.internet.MimeBodyPart;
|
||||
import com.android.email.mail.internet.MimeHeader;
|
||||
import com.android.email.mail.internet.MimeMessage;
|
||||
import com.android.email.mail.internet.MimeMultipart;
|
||||
import com.android.email.mail.internet.MimeUtility;
|
||||
import com.android.email.mail.internet.TextBody;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.provider.AttachmentProvider;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Attachment;
|
||||
|
@ -44,7 +41,6 @@ import android.content.ContentValues;
|
|||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -72,15 +68,6 @@ public class LegacyConversions {
|
|||
/* package */ static final String BODY_QUOTED_PART_FORWARD = "quoted-forward";
|
||||
/* package */ static final String BODY_QUOTED_PART_INTRO = "quoted-intro";
|
||||
|
||||
/**
|
||||
* Standard columns for querying content providers
|
||||
*/
|
||||
private static final String[] ATTACHMENT_META_COLUMNS_PROJECTION = {
|
||||
OpenableColumns.DISPLAY_NAME,
|
||||
OpenableColumns.SIZE
|
||||
};
|
||||
private static final int ATTACHMENT_META_COLUMNS_SIZE = 1;
|
||||
|
||||
/**
|
||||
* Copy field-by-field from a "store" message to a "provider" message
|
||||
* @param message The message we've just downloaded (must be a MimeMessage)
|
||||
|
@ -268,14 +255,13 @@ public class LegacyConversions {
|
|||
* @param context a context for file operations
|
||||
* @param localMessage the attachments will be built against this message
|
||||
* @param attachments the attachments to add
|
||||
* @param upgrading if true, we are upgrading a local account - handle attachments differently
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void updateAttachments(Context context, EmailContent.Message localMessage,
|
||||
ArrayList<Part> attachments, boolean upgrading) throws MessagingException, IOException {
|
||||
ArrayList<Part> attachments) throws MessagingException, IOException {
|
||||
localMessage.mAttachments = null;
|
||||
for (Part attachmentPart : attachments) {
|
||||
addOneAttachment(context, localMessage, attachmentPart, upgrading);
|
||||
addOneAttachment(context, localMessage, attachmentPart);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,11 +281,10 @@ public class LegacyConversions {
|
|||
* @param context a context for file operations
|
||||
* @param localMessage the attachments will be built against this message
|
||||
* @param part a single attachment part from POP or IMAP
|
||||
* @param upgrading true if upgrading a local account - handle attachments differently
|
||||
* @throws IOException
|
||||
*/
|
||||
private static void addOneAttachment(Context context, EmailContent.Message localMessage,
|
||||
Part part, boolean upgrading) throws MessagingException, IOException {
|
||||
Part part) throws MessagingException, IOException {
|
||||
|
||||
Attachment localAttachment = new Attachment();
|
||||
|
||||
|
@ -311,53 +296,13 @@ public class LegacyConversions {
|
|||
name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
|
||||
}
|
||||
|
||||
// Select the URI for the new attachments. For attachments downloaded by the legacy
|
||||
// IMAP/POP code, this is not determined yet, so is null (it will be rewritten below,
|
||||
// or later, when the actual attachment file is created.)
|
||||
//
|
||||
// When upgrading older local accounts, the URI represents a local asset (e.g. a photo)
|
||||
// so we need to preserve the URI.
|
||||
// TODO This works for outgoing messages, where the URI does not change. May need
|
||||
// additional logic to handle the case of rewriting URI for received attachments.
|
||||
Uri contentUri = null;
|
||||
String contentUriString = null;
|
||||
if (upgrading) {
|
||||
Body body = part.getBody();
|
||||
if (body instanceof LocalStore.LocalAttachmentBody) {
|
||||
LocalStore.LocalAttachmentBody localBody = (LocalStore.LocalAttachmentBody) body;
|
||||
contentUri = localBody.getContentUri();
|
||||
if (contentUri != null) {
|
||||
contentUriString = contentUri.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find size, if available, via a number of techniques:
|
||||
// Incoming attachment: Try to pull size from disposition (if not downloaded yet)
|
||||
long size = 0;
|
||||
if (upgrading) {
|
||||
// If upgrading a legacy account, the size must be recaptured from the data source
|
||||
if (contentUri != null) {
|
||||
Cursor metadataCursor = context.getContentResolver().query(contentUri,
|
||||
ATTACHMENT_META_COLUMNS_PROJECTION, null, null, null);
|
||||
if (metadataCursor != null) {
|
||||
try {
|
||||
if (metadataCursor.moveToFirst()) {
|
||||
size = metadataCursor.getInt(ATTACHMENT_META_COLUMNS_SIZE);
|
||||
}
|
||||
} finally {
|
||||
metadataCursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: a downloaded legacy attachment - see if the above code works
|
||||
} else {
|
||||
// Incoming attachment: Try to pull size from disposition (if not downloaded yet)
|
||||
String disposition = part.getDisposition();
|
||||
if (disposition != null) {
|
||||
String s = MimeUtility.getHeaderParameter(disposition, "size");
|
||||
if (s != null) {
|
||||
size = Long.parseLong(s);
|
||||
}
|
||||
String disposition = part.getDisposition();
|
||||
if (disposition != null) {
|
||||
String s = MimeUtility.getHeaderParameter(disposition, "size");
|
||||
if (s != null) {
|
||||
size = Long.parseLong(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,7 +315,7 @@ public class LegacyConversions {
|
|||
localAttachment.mMimeType = part.getMimeType();
|
||||
localAttachment.mSize = size; // May be reset below if file handled
|
||||
localAttachment.mContentId = part.getContentId();
|
||||
localAttachment.mContentUri = contentUriString;
|
||||
localAttachment.mContentUri = null; // Will be rewritten by saveAttachmentBody
|
||||
localAttachment.mMessageKey = localMessage.mId;
|
||||
localAttachment.mLocation = partId;
|
||||
localAttachment.mEncoding = "B"; // TODO - convert other known encodings
|
||||
|
@ -417,9 +362,7 @@ public class LegacyConversions {
|
|||
}
|
||||
|
||||
// If an attachment body was actually provided, we need to write the file now
|
||||
if (!upgrading) {
|
||||
saveAttachmentBody(context, part, localAttachment, localMessage.mAccountKey);
|
||||
}
|
||||
saveAttachmentBody(context, part, localAttachment, localMessage.mAccountKey);
|
||||
|
||||
if (localMessage.mAttachments == null) {
|
||||
localMessage.mAttachments = new ArrayList<Attachment>();
|
||||
|
@ -644,7 +587,7 @@ public class LegacyConversions {
|
|||
/**
|
||||
* Conversion from legacy account to provider account
|
||||
*
|
||||
* Used for backup/restore and for account migration.
|
||||
* Used for backup/restore.
|
||||
*
|
||||
* @param context application context
|
||||
* @param fromAccount the legacy account to convert to modern format
|
||||
|
@ -685,39 +628,7 @@ public class LegacyConversions {
|
|||
}
|
||||
|
||||
/**
|
||||
* Conversion from legacy folder to provider mailbox. Used for account migration.
|
||||
* Note: Many mailbox fields are unused in IMAP & POP accounts.
|
||||
*
|
||||
* @param context application context
|
||||
* @param toAccount the provider account that this folder will be associated with
|
||||
* @param fromFolder the legacy folder to convert to modern format
|
||||
* @return an Account ready to be committed to provider
|
||||
*/
|
||||
public static EmailContent.Mailbox makeMailbox(Context context, EmailContent.Account toAccount,
|
||||
Folder fromFolder) throws MessagingException {
|
||||
EmailContent.Mailbox result = new EmailContent.Mailbox();
|
||||
|
||||
result.mDisplayName = fromFolder.getName();
|
||||
// result.mServerId
|
||||
// result.mParentServerId
|
||||
result.mAccountKey = toAccount.mId;
|
||||
result.mType = inferMailboxTypeFromName(context, fromFolder.getName());
|
||||
// result.mDelimiter
|
||||
// result.mSyncKey
|
||||
// result.mSyncLookback
|
||||
// result.mSyncInterval
|
||||
result.mSyncTime = 0;
|
||||
result.mFlagVisible = true;
|
||||
result.mFlags = 0;
|
||||
result.mVisibleLimit = Email.VISIBLE_LIMIT_DEFAULT;
|
||||
// result.mSyncStatus
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer mailbox type from mailbox name. Used by MessagingController (for live folder sync)
|
||||
* and for legacy account upgrades.
|
||||
* Infer mailbox type from mailbox name. Used by MessagingController (for live folder sync).
|
||||
*/
|
||||
public static synchronized int inferMailboxTypeFromName(Context context, String mailboxName) {
|
||||
if (sServerMailboxNames.size() == 0) {
|
||||
|
|
|
@ -1012,8 +1012,7 @@ public class MessagingController implements Runnable {
|
|||
saveOrUpdate(body, context);
|
||||
|
||||
// process (and save) attachments
|
||||
LegacyConversions.updateAttachments(context, localMessage,
|
||||
attachments, false);
|
||||
LegacyConversions.updateAttachments(context, localMessage, attachments);
|
||||
|
||||
// One last update of message with two updated flags
|
||||
localMessage.mFlagLoaded = loadStatus;
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* An activity to handle old-style (Android <= 1.6) desktop shortcut.
|
||||
*/
|
||||
public class FolderMessageList extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
openInbox();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
finish();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void openInbox() {
|
||||
// If it's the first time, we need to upgrade all accounts.
|
||||
if (UpgradeAccounts.doBulkUpgradeIfNecessary(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = getIntent().getData();
|
||||
|
||||
// Verify the format.
|
||||
if (uri == null || !"content".equals(uri.getScheme())
|
||||
|| !"accounts".equals(uri.getAuthority())) {
|
||||
return;
|
||||
}
|
||||
String uuid = uri.getPath();
|
||||
if (uuid.length() > 0) {
|
||||
uuid = uuid.substring(1); // remove the beginning '/'.
|
||||
}
|
||||
if (TextUtils.isEmpty(uuid)) {
|
||||
return;
|
||||
}
|
||||
MessageList.actionOpenAccountInboxUuid(this, uuid);
|
||||
}
|
||||
}
|
|
@ -1,880 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.activity;
|
||||
|
||||
import com.android.email.Account;
|
||||
import com.android.email.Email;
|
||||
import com.android.email.LegacyConversions;
|
||||
import com.android.email.Preferences;
|
||||
import com.android.email.R;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.activity.setup.AccountSettingsUtils;
|
||||
import com.android.email.activity.setup.AccountSettingsUtils.Provider;
|
||||
import com.android.email.mail.FetchProfile;
|
||||
import com.android.email.mail.Flag;
|
||||
import com.android.email.mail.Folder;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Part;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.mail.internet.MimeUtility;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.AccountColumns;
|
||||
import com.android.email.provider.EmailContent.HostAuth;
|
||||
import com.android.email.provider.EmailContent.Mailbox;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* *** Account upgrade has been disabled for the tablet release ***
|
||||
*
|
||||
* TODO Remove it and related code, when we no longer have to support upgrade from donut.
|
||||
*
|
||||
* This activity will be used whenever we have a large/slow bulk upgrade operation.
|
||||
*
|
||||
* The general strategy is to iterate through the legacy accounts, convert them one-by-one, and
|
||||
* then delete them. The loop is very conservative; If there is any problem, the bias will be
|
||||
* to abandon the conversion and let the account be deleted. We never want to get stuck here, and
|
||||
* we never want to run more than once (on a device being upgraded from 1.6). After this code
|
||||
* runs, there should be zero legacy accounts.
|
||||
*
|
||||
* Note: It's preferable to check for "accounts needing upgrade" before launching this
|
||||
* activity, so as to not waste time before every launch.
|
||||
*
|
||||
* Note: This activity is set (in the manifest) to disregard configuration changes (e.g. rotation).
|
||||
* This allows it to continue through without restarting.
|
||||
* Do not attempt to define orientation-specific resources, they won't be loaded.
|
||||
*
|
||||
* TODO: Read pending events and convert them to things like updates or deletes in the DB
|
||||
* TODO: Smarter cleanup of SSL/TLS situation, since certificates may be bad (see design spec)
|
||||
*/
|
||||
public class UpgradeAccounts extends ListActivity implements OnClickListener {
|
||||
|
||||
/** DO NOT CHECK IN AS 'TRUE' - DEVELOPMENT ONLY */
|
||||
private static final boolean DEBUG_FORCE_UPGRADES = false;
|
||||
|
||||
private AccountInfo[] mLegacyAccounts;
|
||||
private UIHandler mHandler = new UIHandler();
|
||||
private AccountsAdapter mAdapter;
|
||||
private ListView mListView;
|
||||
private Button mProceedButton;
|
||||
private ConversionTask mConversionTask;
|
||||
|
||||
// These are used to hold off restart of this activity while worker is still busy
|
||||
private static final Object sConversionInProgress = new Object();
|
||||
private static boolean sConversionHasRun = false;
|
||||
|
||||
/** This projection is for looking up accounts by their legacy UUID */
|
||||
private static final String WHERE_ACCOUNT_UUID_IS = AccountColumns.COMPATIBILITY_UUID + "=?";
|
||||
|
||||
private static void actionStart(Context context) {
|
||||
Intent i = new Intent(context, UpgradeAccounts.class);
|
||||
context.startActivity(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
ActivityHelper.debugSetWindowFlags(this);
|
||||
|
||||
Preferences p = Preferences.getPreferences(this);
|
||||
Account[] legacyAccounts = p.getAccounts();
|
||||
if (legacyAccounts.length == 0) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
loadAccountInfoArray(legacyAccounts);
|
||||
|
||||
Log.d(Email.LOG_TAG, "*** Preparing to upgrade " +
|
||||
Integer.toString(mLegacyAccounts.length) + " accounts");
|
||||
|
||||
setContentView(R.layout.upgrade_accounts);
|
||||
mListView = getListView();
|
||||
mProceedButton = (Button) findViewById(R.id.action_button);
|
||||
mProceedButton.setEnabled(false);
|
||||
mProceedButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateList();
|
||||
|
||||
// Start the big conversion engine
|
||||
mConversionTask = new ConversionTask(mLegacyAccounts);
|
||||
mConversionTask.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
Utility.cancelTask(mConversionTask, false); // false = Don't interrupt running task
|
||||
mConversionTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stopgap measure to prevent monkey or zealous user from exiting while we're still at work.
|
||||
*/
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!mProceedButton.isEnabled()) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.action_button:
|
||||
onClickOk();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onClickOk() {
|
||||
Welcome.actionStart(UpgradeAccounts.this);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void updateList() {
|
||||
mAdapter = new AccountsAdapter();
|
||||
getListView().setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
private static class AccountInfo {
|
||||
Account account;
|
||||
int maxProgress;
|
||||
int progress;
|
||||
String errorMessage; // set/read by handler - UI thread only
|
||||
boolean isError; // set/read by worker thread
|
||||
|
||||
public AccountInfo(Account legacyAccount) {
|
||||
account = legacyAccount;
|
||||
maxProgress = 0;
|
||||
progress = 0;
|
||||
errorMessage = null;
|
||||
isError = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAccountInfoArray(Account[] legacyAccounts) {
|
||||
mLegacyAccounts = new AccountInfo[legacyAccounts.length];
|
||||
for (int i = 0; i < legacyAccounts.length; i++) {
|
||||
AccountInfo ai = new AccountInfo(legacyAccounts[i]);
|
||||
mLegacyAccounts[i] = ai;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
TextView displayName;
|
||||
ProgressBar progress;
|
||||
TextView errorReport;
|
||||
}
|
||||
|
||||
class AccountsAdapter extends BaseAdapter {
|
||||
final LayoutInflater mInflater;
|
||||
|
||||
AccountsAdapter() {
|
||||
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mLegacyAccounts.length;
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mLegacyAccounts[position];
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, position);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.upgrade_accounts_item, parent, false);
|
||||
ViewHolder h = new ViewHolder();
|
||||
h.displayName = (TextView) v.findViewById(R.id.name);
|
||||
h.progress = (ProgressBar) v.findViewById(R.id.progress);
|
||||
h.errorReport = (TextView) v.findViewById(R.id.error);
|
||||
v.setTag(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void bindView(View view, int position) {
|
||||
ViewHolder vh = (ViewHolder) view.getTag();
|
||||
AccountInfo ai = mLegacyAccounts[position];
|
||||
vh.displayName.setText(ai.account.getDescription());
|
||||
if (ai.errorMessage == null) {
|
||||
vh.errorReport.setVisibility(View.GONE);
|
||||
vh.progress.setVisibility(View.VISIBLE);
|
||||
vh.progress.setMax(ai.maxProgress);
|
||||
vh.progress.setProgress(ai.progress);
|
||||
} else {
|
||||
vh.progress.setVisibility(View.GONE);
|
||||
vh.errorReport.setVisibility(View.VISIBLE);
|
||||
vh.errorReport.setText(ai.errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for updating UI from async workers
|
||||
*
|
||||
* TODO: I don't know the right paradigm for updating a progress bar in a ListView. I'd
|
||||
* like to be able to say, "update it if it's visible, skip it if it's not visible."
|
||||
*/
|
||||
class UIHandler extends Handler {
|
||||
private static final int MSG_SET_MAX = 1;
|
||||
private static final int MSG_SET_PROGRESS = 2;
|
||||
private static final int MSG_INC_PROGRESS = 3;
|
||||
private static final int MSG_ERROR = 4;
|
||||
|
||||
@Override
|
||||
public void handleMessage(android.os.Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SET_MAX:
|
||||
mLegacyAccounts[msg.arg1].maxProgress = msg.arg2;
|
||||
mListView.invalidateViews(); // find a less annoying way to do that
|
||||
break;
|
||||
case MSG_SET_PROGRESS:
|
||||
mLegacyAccounts[msg.arg1].progress = msg.arg2;
|
||||
mListView.invalidateViews(); // find a less annoying way to do that
|
||||
break;
|
||||
case MSG_INC_PROGRESS:
|
||||
mLegacyAccounts[msg.arg1].progress += msg.arg2;
|
||||
mListView.invalidateViews(); // find a less annoying way to do that
|
||||
break;
|
||||
case MSG_ERROR:
|
||||
mLegacyAccounts[msg.arg1].errorMessage = (String) msg.obj;
|
||||
mListView.invalidateViews(); // find a less annoying way to do that
|
||||
mProceedButton.setEnabled(true);
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxProgress(int accountNum, int max) {
|
||||
android.os.Message msg = android.os.Message.obtain();
|
||||
msg.what = MSG_SET_MAX;
|
||||
msg.arg1 = accountNum;
|
||||
msg.arg2 = max;
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
public void setProgress(int accountNum, int progress) {
|
||||
android.os.Message msg = android.os.Message.obtain();
|
||||
msg.what = MSG_SET_PROGRESS;
|
||||
msg.arg1 = accountNum;
|
||||
msg.arg2 = progress;
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
public void incProgress(int accountNum) {
|
||||
incProgress(accountNum, 1);
|
||||
}
|
||||
|
||||
public void incProgress(int accountNum, int incrementBy) {
|
||||
if (incrementBy == 0) return;
|
||||
android.os.Message msg = android.os.Message.obtain();
|
||||
msg.what = MSG_INC_PROGRESS;
|
||||
msg.arg1 = accountNum;
|
||||
msg.arg2 = incrementBy;
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
// Note: also enables the "OK" button, so we pause when complete
|
||||
public void error(int accountNum, String error) {
|
||||
android.os.Message msg = android.os.Message.obtain();
|
||||
msg.what = MSG_ERROR;
|
||||
msg.arg1 = accountNum;
|
||||
msg.obj = error;
|
||||
sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything above was UI plumbing. This is the meat of this class - a conversion
|
||||
* engine to rebuild accounts from the "LocalStore" (pre Android 2.0) format to the
|
||||
* "Provider" (2.0 and beyond) format.
|
||||
*/
|
||||
private class ConversionTask extends AsyncTask<Void, Void, Void> {
|
||||
UpgradeAccounts.AccountInfo[] mAccountInfo;
|
||||
final Context mContext;
|
||||
final Preferences mPreferences;
|
||||
|
||||
public ConversionTask(UpgradeAccounts.AccountInfo[] accountInfo) {
|
||||
// TODO: should I copy this?
|
||||
mAccountInfo = accountInfo;
|
||||
mContext = UpgradeAccounts.this;
|
||||
mPreferences = Preferences.getPreferences(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
// Globally synchronize this entire block to prevent it from running in multiple
|
||||
// threads. this is used in case we wind up relaunching during a conversion.
|
||||
// If this is anything but the first thread, sConversionHasRun will be set and we'll
|
||||
// exit immediately when it's all over.
|
||||
synchronized (sConversionInProgress) {
|
||||
if (sConversionHasRun) {
|
||||
return null;
|
||||
}
|
||||
sConversionHasRun = true;
|
||||
|
||||
UIHandler handler = UpgradeAccounts.this.mHandler;
|
||||
// Step 1: Analyze accounts and generate progress max values
|
||||
for (int i = 0; i < mAccountInfo.length; i++) {
|
||||
int estimate = UpgradeAccounts.estimateWork(mContext, mAccountInfo[i].account);
|
||||
if (estimate == -1) {
|
||||
mAccountInfo[i].isError = true;
|
||||
mHandler.error(i, mContext.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
UpgradeAccounts.this.mHandler.setMaxProgress(i, estimate);
|
||||
}
|
||||
|
||||
// Step 2: Scrub accounts, deleting anything we're not keeping to reclaim storage
|
||||
for (int i = 0; i < mAccountInfo.length; i++) {
|
||||
if (!mAccountInfo[i].isError) {
|
||||
boolean ok = scrubAccount(mContext, mAccountInfo[i].account, i, handler);
|
||||
if (!ok) {
|
||||
mAccountInfo[i].isError = true;
|
||||
mHandler.error(i, mContext.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Copy accounts (and delete old accounts). POP accounts first.
|
||||
// Note: We don't check error condition here because we still want to
|
||||
// delete the remaining parts of all accounts (even if in error condition).
|
||||
for (int i = 0; i < mAccountInfo.length; i++) {
|
||||
AccountInfo info = mAccountInfo[i];
|
||||
copyAndDeleteAccount(info, i, handler, Store.STORE_SCHEME_POP3);
|
||||
}
|
||||
// IMAP accounts next.
|
||||
for (int i = 0; i < mAccountInfo.length; i++) {
|
||||
AccountInfo info = mAccountInfo[i];
|
||||
copyAndDeleteAccount(info, i, handler, Store.STORE_SCHEME_IMAP);
|
||||
}
|
||||
|
||||
// Step 4: Enable app-wide features such as composer, and start mail service(s)
|
||||
Email.setServicesEnabledSync(mContext);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy and delete one account (helper for doInBackground). Can select accounts by type
|
||||
* to force conversion of one or another type only.
|
||||
*/
|
||||
private void copyAndDeleteAccount(AccountInfo info, int i, UIHandler handler, String type) {
|
||||
try {
|
||||
if (type != null) {
|
||||
String storeUri = info.account.getStoreUri();
|
||||
boolean isType = storeUri.startsWith(type);
|
||||
if (!isType) {
|
||||
return; // skip this account
|
||||
}
|
||||
}
|
||||
// Don't try copying if this account is already in error state
|
||||
if (!info.isError) {
|
||||
copyAccount(mContext, info.account, i, handler);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while copying account " + e);
|
||||
mHandler.error(i, mContext.getString(R.string.upgrade_accounts_error));
|
||||
info.isError = true;
|
||||
}
|
||||
// best effort to delete it (whether copied or not)
|
||||
try {
|
||||
deleteAccountStore(mContext, info.account, i, handler);
|
||||
info.account.delete(mPreferences);
|
||||
} catch (RuntimeException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while deleting account " + e);
|
||||
// No user notification is required here - we're done
|
||||
}
|
||||
// jam the progress indicator to mark account "complete" (in case est was wrong)
|
||||
handler.setProgress(i, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
if (!isCancelled()) {
|
||||
// if there were no errors, we never enabled the OK button, but
|
||||
// we'll just proceed through anyway and return to the Welcome activity
|
||||
if (!mProceedButton.isEnabled()) {
|
||||
onClickOk();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate the work required to convert an account.
|
||||
* 1 (account) + # folders + # messages + # attachments
|
||||
* @return conversion operations estimate, or -1 if there's any problem
|
||||
*/
|
||||
/* package */ static int estimateWork(Context context, Account account) {
|
||||
int estimate = 1; // account
|
||||
try {
|
||||
LocalStore store = LocalStore.newInstance(account.getLocalStoreUri(), context, null);
|
||||
Folder[] folders = store.getPersonalNamespaces();
|
||||
estimate += folders.length;
|
||||
for (int i = 0; i < folders.length; i++) {
|
||||
Folder folder = folders[i];
|
||||
folder.open(Folder.OpenMode.READ_ONLY, null);
|
||||
estimate += folder.getMessageCount();
|
||||
folder.close(false);
|
||||
}
|
||||
estimate += store.getStoredAttachmentCount();
|
||||
store.close();
|
||||
} catch (MessagingException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while estimating account size " + e);
|
||||
return -1;
|
||||
} catch (RuntimeException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while estimating account size " + e);
|
||||
return -1;
|
||||
}
|
||||
return estimate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean out an account.
|
||||
*
|
||||
* For IMAP: Anything we can reload from server, we delete. This reduces the risk of running
|
||||
* out of disk space by copying everything.
|
||||
* For POP: Delete the trash folder (which we won't bring forward).
|
||||
* @return true if successful, false if any kind of error
|
||||
*/
|
||||
/* package */ static boolean scrubAccount(Context context, Account account, int accountNum,
|
||||
UIHandler handler) {
|
||||
try {
|
||||
String storeUri = account.getStoreUri();
|
||||
boolean isImap = storeUri.startsWith(Store.STORE_SCHEME_IMAP);
|
||||
LocalStore store = LocalStore.newInstance(account.getLocalStoreUri(), context, null);
|
||||
Folder[] folders = store.getPersonalNamespaces();
|
||||
for (Folder folder : folders) {
|
||||
folder.open(Folder.OpenMode.READ_ONLY, null);
|
||||
String folderName = folder.getName();
|
||||
if ("drafts".equalsIgnoreCase(folderName)) {
|
||||
// do not delete drafts
|
||||
} else if ("outbox".equalsIgnoreCase(folderName)) {
|
||||
// do not delete outbox
|
||||
} else if ("sent".equalsIgnoreCase(folderName)) {
|
||||
// do not delete sent
|
||||
} else if (isImap || "trash".equalsIgnoreCase(folderName)) {
|
||||
Log.d(Email.LOG_TAG, "Scrub " + account.getDescription() + "." + folderName);
|
||||
// for all other folders, delete the folder (and its messages & attachments)
|
||||
int messageCount = folder.getMessageCount();
|
||||
folder.delete(true);
|
||||
if (handler != null) {
|
||||
handler.incProgress(accountNum, 1 + messageCount);
|
||||
}
|
||||
}
|
||||
folder.close(false);
|
||||
}
|
||||
int pruned = store.pruneCachedAttachments();
|
||||
if (handler != null) {
|
||||
handler.incProgress(accountNum, pruned);
|
||||
}
|
||||
store.close();
|
||||
} catch (MessagingException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while scrubbing account " + e);
|
||||
return false;
|
||||
} catch (RuntimeException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while scrubbing account " + e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class FolderConversion {
|
||||
final Folder folder;
|
||||
final EmailContent.Mailbox mailbox;
|
||||
|
||||
public FolderConversion(Folder _folder, EmailContent.Mailbox _mailbox) {
|
||||
folder = _folder;
|
||||
mailbox = _mailbox;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an account.
|
||||
*/
|
||||
/* package */ static void copyAccount(Context context, Account account, int accountNum,
|
||||
UIHandler handler) {
|
||||
// If already exists- just skip it
|
||||
int existCount = EmailContent.count(context, EmailContent.Account.CONTENT_URI,
|
||||
WHERE_ACCOUNT_UUID_IS, new String[] { account.getUuid() });
|
||||
if (existCount > 0) {
|
||||
Log.d(Email.LOG_TAG, "No conversion, account exists: " + account.getDescription());
|
||||
if (handler != null) {
|
||||
handler.error(accountNum, context.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Create the new account and write it
|
||||
EmailContent.Account newAccount = LegacyConversions.makeAccount(context, account);
|
||||
cleanupConnections(context, newAccount, account);
|
||||
newAccount.save(context);
|
||||
if (handler != null) {
|
||||
handler.incProgress(accountNum);
|
||||
}
|
||||
|
||||
// copy the folders, making a set of them as we go, and recording a few that we
|
||||
// need to process first (highest priority for saving the messages)
|
||||
HashSet<FolderConversion> conversions = new HashSet<FolderConversion>();
|
||||
FolderConversion drafts = null;
|
||||
FolderConversion outbox = null;
|
||||
FolderConversion sent = null;
|
||||
LocalStore store = null;
|
||||
try {
|
||||
store = LocalStore.newInstance(account.getLocalStoreUri(), context, null);
|
||||
Folder[] folders = store.getPersonalNamespaces();
|
||||
for (Folder folder : folders) {
|
||||
String folderName = null;
|
||||
try {
|
||||
folder.open(Folder.OpenMode.READ_ONLY, null);
|
||||
folderName = folder.getName();
|
||||
Log.d(Email.LOG_TAG, "Copy " + account.getDescription() + "." + folderName);
|
||||
EmailContent.Mailbox mailbox =
|
||||
LegacyConversions.makeMailbox(context, newAccount, folder);
|
||||
mailbox.save(context);
|
||||
if (handler != null) {
|
||||
handler.incProgress(accountNum);
|
||||
}
|
||||
folder.close(false);
|
||||
// Now record the conversion, to come back and do the messages
|
||||
FolderConversion conversion = new FolderConversion(folder, mailbox);
|
||||
conversions.add(conversion);
|
||||
switch (mailbox.mType) {
|
||||
case Mailbox.TYPE_DRAFTS:
|
||||
drafts = conversion;
|
||||
break;
|
||||
case Mailbox.TYPE_OUTBOX:
|
||||
outbox = conversion;
|
||||
break;
|
||||
case Mailbox.TYPE_SENT:
|
||||
sent = conversion;
|
||||
break;
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
// We make a best-effort attempt at each folder, so even if this one fails,
|
||||
// we'll try to keep going.
|
||||
Log.d(Email.LOG_TAG, "Exception copying folder " + folderName + ": " + e);
|
||||
if (handler != null) {
|
||||
handler.error(accountNum,
|
||||
context.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy the messages, starting with the most critical folders, and then doing the rest
|
||||
// outbox & drafts are the most important, as they don't exist anywhere else. we also
|
||||
// process local (outgoing) attachments here
|
||||
if (outbox != null) {
|
||||
copyMessages(context, outbox, true, newAccount, accountNum, handler);
|
||||
conversions.remove(outbox);
|
||||
}
|
||||
if (drafts != null) {
|
||||
copyMessages(context, drafts, true, newAccount, accountNum, handler);
|
||||
conversions.remove(drafts);
|
||||
}
|
||||
if (sent != null) {
|
||||
copyMessages(context, sent, true, newAccount, accountNum, handler);
|
||||
conversions.remove(outbox);
|
||||
}
|
||||
// Now handle any remaining folders. For incoming folders we skip attachments, as they
|
||||
// can be reloaded from the server.
|
||||
for (FolderConversion conversion : conversions) {
|
||||
copyMessages(context, conversion, false, newAccount, accountNum, handler);
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while copying folders " + e);
|
||||
// Couldn't copy folders at all
|
||||
if (handler != null) {
|
||||
handler.error(accountNum, context.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
} finally {
|
||||
if (store != null) {
|
||||
store.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all messages in a given folder
|
||||
*
|
||||
* @param context a system context
|
||||
* @param conversion a folder->mailbox conversion record
|
||||
* @param localAttachments true if the attachments refer to local data (to be sent)
|
||||
* @param newAccount the id of the newly-created account
|
||||
* @param accountNum the UI list # of the account
|
||||
* @param handler the handler for updating the UI
|
||||
*/
|
||||
/* package */ static void copyMessages(Context context, FolderConversion conversion,
|
||||
boolean localAttachments, EmailContent.Account newAccount, int accountNum,
|
||||
UIHandler handler) {
|
||||
try {
|
||||
boolean makeDeleteSentinels = (conversion.mailbox.mType == Mailbox.TYPE_INBOX) &&
|
||||
(newAccount.getDeletePolicy() == EmailContent.Account.DELETE_POLICY_NEVER);
|
||||
conversion.folder.open(Folder.OpenMode.READ_ONLY, null);
|
||||
Message[] oldMessages = conversion.folder.getMessages(null);
|
||||
for (Message oldMessage : oldMessages) {
|
||||
Exception e = null;
|
||||
try {
|
||||
// load message data from legacy Store
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
conversion.folder.fetch(new Message[] { oldMessage }, fp, null);
|
||||
EmailContent.Message newMessage = new EmailContent.Message();
|
||||
if (makeDeleteSentinels && oldMessage.isSet(Flag.DELETED)) {
|
||||
// Special case for POP3 locally-deleted messages.
|
||||
// Creates a local "deleted message sentinel" which hides the message
|
||||
// Echos provider code in MessagingController.processPendingMoveToTrash()
|
||||
newMessage.mAccountKey = newAccount.mId;
|
||||
newMessage.mMailboxKey = conversion.mailbox.mId;
|
||||
newMessage.mFlagLoaded = EmailContent.Message.FLAG_LOADED_DELETED;
|
||||
newMessage.mFlagRead = true;
|
||||
newMessage.mServerId = oldMessage.getUid();
|
||||
newMessage.save(context);
|
||||
} else {
|
||||
// Main case for converting real messages with bodies & attachments
|
||||
// convert message (headers)
|
||||
LegacyConversions.updateMessageFields(newMessage, oldMessage,
|
||||
newAccount.mId, conversion.mailbox.mId);
|
||||
// convert body (text)
|
||||
EmailContent.Body newBody = new EmailContent.Body();
|
||||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(oldMessage, viewables, attachments);
|
||||
LegacyConversions.updateBodyFields(newBody, newMessage, viewables);
|
||||
// commit changes so far so we have real id's
|
||||
newMessage.save(context);
|
||||
newBody.save(context);
|
||||
// convert attachments
|
||||
if (localAttachments) {
|
||||
// These are references to local data, and should create records only
|
||||
// (e.g. the content URI). No files should be created.
|
||||
LegacyConversions.updateAttachments(context, newMessage, attachments,
|
||||
true);
|
||||
}
|
||||
}
|
||||
// done
|
||||
if (handler != null) {
|
||||
handler.incProgress(accountNum);
|
||||
}
|
||||
} catch (MessagingException me) {
|
||||
e = me;
|
||||
} catch (IOException ioe) {
|
||||
e = ioe;
|
||||
}
|
||||
if (e != null) {
|
||||
Log.d(Email.LOG_TAG, "Exception copying message " + oldMessage.getSubject()
|
||||
+ ": "+ e);
|
||||
if (handler != null) {
|
||||
handler.error(accountNum,
|
||||
context.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
// Couldn't copy folder at all
|
||||
Log.d(Email.LOG_TAG, "Exception while copying messages in " +
|
||||
conversion.folder.toString() + ": " + e);
|
||||
if (handler != null) {
|
||||
handler.error(accountNum, context.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an account
|
||||
*/
|
||||
/* package */ static void deleteAccountStore(Context context, Account account, int accountNum,
|
||||
UIHandler handler) {
|
||||
try {
|
||||
Store store = LocalStore.newInstance(account.getLocalStoreUri(), context, null);
|
||||
store.delete();
|
||||
// delete() closes the store
|
||||
} catch (MessagingException e) {
|
||||
Log.d(Email.LOG_TAG, "Exception while deleting account " + e);
|
||||
if (handler != null) {
|
||||
handler.error(accountNum, context.getString(R.string.upgrade_accounts_error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup SSL, TLS, etc for each converted account.
|
||||
*/
|
||||
/* package */ static void cleanupConnections(Context context, EmailContent.Account newAccount,
|
||||
Account account) {
|
||||
// 1. Look up provider for this email address
|
||||
String email = newAccount.mEmailAddress;
|
||||
int atSignPos = email.lastIndexOf('@');
|
||||
String domain = email.substring(atSignPos + 1);
|
||||
Provider p = AccountSettingsUtils.findProviderForDomain(context, domain);
|
||||
|
||||
// 2. If provider found, just use its settings (overriding what user had)
|
||||
// This is drastic but most reliable. Note: This also benefits from newer provider
|
||||
// data that might be found in a vendor policy module.
|
||||
if (p != null) {
|
||||
// Incoming
|
||||
try {
|
||||
URI incomingUriTemplate = p.incomingUriTemplate;
|
||||
String incomingUsername = newAccount.mHostAuthRecv.mLogin;
|
||||
String incomingPassword = newAccount.mHostAuthRecv.mPassword;
|
||||
URI incomingUri = new URI(incomingUriTemplate.getScheme(), incomingUsername + ":"
|
||||
+ incomingPassword, incomingUriTemplate.getHost(),
|
||||
incomingUriTemplate.getPort(), incomingUriTemplate.getPath(), null, null);
|
||||
newAccount.mHostAuthRecv.setStoreUri(incomingUri.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
// Ignore - just use the data we copied across (for better or worse)
|
||||
}
|
||||
// Outgoing
|
||||
try {
|
||||
URI outgoingUriTemplate = p.outgoingUriTemplate;
|
||||
String outgoingUsername = newAccount.mHostAuthSend.mLogin;
|
||||
String outgoingPassword = newAccount.mHostAuthSend.mPassword;
|
||||
URI outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + ":"
|
||||
+ outgoingPassword, outgoingUriTemplate.getHost(),
|
||||
outgoingUriTemplate.getPort(), outgoingUriTemplate.getPath(), null, null);
|
||||
newAccount.mHostAuthSend.setStoreUri(outgoingUri.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
// Ignore - just use the data we copied across (for better or worse)
|
||||
}
|
||||
Log.d(Email.LOG_TAG, "Rewriting connection details for " + account.getDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Otherwise, use simple heuristics to adjust connection and attempt to keep it
|
||||
// reliable. NOTE: These are the "legacy" ssl/tls encodings, not the ones in
|
||||
// the current provider.
|
||||
newAccount.mHostAuthRecv.mFlags |= HostAuth.FLAG_TRUST_ALL_CERTIFICATES;
|
||||
String receiveUri = account.getStoreUri();
|
||||
if (receiveUri.contains("+ssl+")) {
|
||||
// non-optional SSL - keep as is, with trust all
|
||||
} else if (receiveUri.contains("+ssl")) {
|
||||
// optional SSL - TBD
|
||||
} else if (receiveUri.contains("+tls+")) {
|
||||
// non-optional TLS - keep as is, with trust all
|
||||
} else if (receiveUri.contains("+tls")) {
|
||||
// optional TLS - TBD
|
||||
}
|
||||
newAccount.mHostAuthSend.mFlags |= HostAuth.FLAG_TRUST_ALL_CERTIFICATES;
|
||||
String sendUri = account.getSenderUri();
|
||||
if (sendUri.contains("+ssl+")) {
|
||||
// non-optional SSL - keep as is, with trust all
|
||||
} else if (sendUri.contains("+ssl")) {
|
||||
// optional SSL - TBD
|
||||
} else if (sendUri.contains("+tls+")) {
|
||||
// non-optional TLS - keep as is, with trust all
|
||||
} else if (sendUri.contains("+tls")) {
|
||||
// optional TLS - TBD
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk upgrade old accounts if exist.
|
||||
*
|
||||
* @return true if bulk upgrade has started. false otherwise.
|
||||
*/
|
||||
/* package */ static boolean doBulkUpgradeIfNecessary(Context context) {
|
||||
// if (bulkUpgradesRequired(context, Preferences.getPreferences(context))) {
|
||||
// UpgradeAccounts.actionStart(context);
|
||||
// return true;
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for bulk upgrades and return true if necessary
|
||||
*
|
||||
* @return true if upgrades required (old accounts exist). false otherwise.
|
||||
*/
|
||||
private static boolean bulkUpgradesRequired(Context context, Preferences preferences) {
|
||||
if (DEBUG_FORCE_UPGRADES) {
|
||||
// build at least one fake account
|
||||
Account fake = new Account(context);
|
||||
fake.setDescription("Fake Account");
|
||||
fake.setEmail("user@gmail.com");
|
||||
fake.setName("First Last");
|
||||
fake.setSenderUri("smtp://user:password@smtp.gmail.com");
|
||||
fake.setStoreUri("imap://user:password@imap.gmail.com");
|
||||
fake.save(preferences);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 1. Get list of legacy accounts and look for any non-backup entries
|
||||
Account[] legacyAccounts = preferences.getAccounts();
|
||||
if (legacyAccounts.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Look at the first legacy account and decide what to do
|
||||
// We only need to look at the first: If it's not a backup account, then it's a true
|
||||
// legacy account, and there are one or more accounts needing upgrade. If it is a backup
|
||||
// account, then we know for sure that there are no legacy accounts (backup deletes all
|
||||
// old accounts, and indicates that "modern" code has already run on this device.)
|
||||
if (0 != (legacyAccounts[0].getBackupFlags() & Account.BACKUP_FLAGS_IS_BACKUP)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -156,13 +156,6 @@ public class Welcome extends Activity {
|
|||
// Reset the "accounts changed" notification, now that we're here
|
||||
Email.setNotifyUiAccountsChanged(false);
|
||||
|
||||
// Quickly check for bulk upgrades (from older app versions) and switch to the
|
||||
// upgrade activity if necessary
|
||||
if (UpgradeAccounts.doBulkUpgradeIfNecessary(this)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore accounts, if it has not happened already
|
||||
// NOTE: This is blocking, which it should not be (in the UI thread)
|
||||
// We're going to live with this for the short term and replace with something
|
||||
|
|
|
@ -16,15 +16,6 @@
|
|||
|
||||
package com.android.email.mail.internet;
|
||||
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.Multipart;
|
||||
import com.android.email.mail.Part;
|
||||
import com.android.email.mail.store.LocalStore.LocalAttachmentBodyPart;
|
||||
import com.android.email.provider.AttachmentProvider;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -34,51 +25,6 @@ public class EmailHtmlUtil {
|
|||
// multiple continuous spaces.
|
||||
private static final Pattern PLAIN_TEXT_TO_ESCAPE = Pattern.compile("[<>&]| {2,}|\r?\n");
|
||||
|
||||
//TODO: make resolveInlineImage() work in the new content provider model.
|
||||
/**
|
||||
* Resolve content-id reference in src attribute of img tag to AttachmentProvider's
|
||||
* content uri. This method calls itself recursively at most the number of
|
||||
* LocalAttachmentPart that mime type is image and has content id.
|
||||
* The attribute src="cid:content_id" is resolved as src="content://...".
|
||||
* This method is package scope for testing purpose.
|
||||
*
|
||||
* @param text html email text
|
||||
* @param part mime part which may contain inline image
|
||||
* @return html text in which src attribute of img tag may be replaced with content uri
|
||||
*/
|
||||
public static String resolveInlineImage(
|
||||
ContentResolver resolver, long accountId, String text, Part part, int depth)
|
||||
throws MessagingException {
|
||||
// avoid too deep recursive call.
|
||||
if (depth >= 10 || text == null) {
|
||||
return text;
|
||||
}
|
||||
String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
|
||||
String contentId = part.getContentId();
|
||||
if (contentType.startsWith("image/") &&
|
||||
contentId != null &&
|
||||
part instanceof LocalAttachmentBodyPart) {
|
||||
LocalAttachmentBodyPart attachment = (LocalAttachmentBodyPart)part;
|
||||
Uri attachmentUri =
|
||||
AttachmentProvider.getAttachmentUri(accountId, attachment.getAttachmentId());
|
||||
Uri contentUri =
|
||||
AttachmentProvider.resolveAttachmentIdToContentUri(resolver, attachmentUri);
|
||||
// Regexp which matches ' src="cid:contentId"'.
|
||||
String contentIdRe = "\\s+(?i)src=\"cid(?-i):\\Q" + contentId + "\\E\"";
|
||||
// Replace all occurrences of src attribute with ' src="content://contentUri"'.
|
||||
text = text.replaceAll(contentIdRe, " src=\"" + contentUri + "\"");
|
||||
}
|
||||
|
||||
if (part.getBody() instanceof Multipart) {
|
||||
Multipart mp = (Multipart)part.getBody();
|
||||
for (int i = 0; i < mp.getCount(); i++) {
|
||||
text = resolveInlineImage(resolver, accountId, text, mp.getBodyPart(i), depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape some special character as HTML escape sequence.
|
||||
*
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,11 +17,8 @@
|
|||
package com.android.email;
|
||||
|
||||
import com.android.email.mail.Address;
|
||||
import com.android.email.mail.Body;
|
||||
import com.android.email.mail.BodyPart;
|
||||
import com.android.email.mail.Flag;
|
||||
import com.android.email.mail.Folder;
|
||||
import com.android.email.mail.Folder.OpenMode;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.Message.RecipientType;
|
||||
import com.android.email.mail.MessageTestUtils;
|
||||
|
@ -34,11 +31,8 @@ import com.android.email.mail.internet.MimeHeader;
|
|||
import com.android.email.mail.internet.MimeMessage;
|
||||
import com.android.email.mail.internet.MimeUtility;
|
||||
import com.android.email.mail.internet.TextBody;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.mail.store.LocalStoreUnitTests;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Attachment;
|
||||
import com.android.email.provider.EmailContent.Mailbox;
|
||||
import com.android.email.provider.EmailProvider;
|
||||
import com.android.email.provider.ProviderTestUtils;
|
||||
|
||||
|
@ -213,13 +207,13 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
// test 1: legacy message using content-type:name style for name
|
||||
final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
|
||||
"local-message", accountId, mailboxId, false, true, mProviderContext);
|
||||
final Message legacyMessage = prepareLegacyMessageWithAttachments(2, false, false);
|
||||
final Message legacyMessage = prepareLegacyMessageWithAttachments(2, false);
|
||||
convertAndCheckcheckAddedAttachments(localMessage, legacyMessage);
|
||||
|
||||
// test 2: legacy message using content-disposition:filename style for name
|
||||
final EmailContent.Message localMessage2 = ProviderTestUtils.setupMessage(
|
||||
"local-message", accountId, mailboxId, false, true, mProviderContext);
|
||||
final Message legacyMessage2 = prepareLegacyMessageWithAttachments(2, false, true);
|
||||
final Message legacyMessage2 = prepareLegacyMessageWithAttachments(2, true);
|
||||
convertAndCheckcheckAddedAttachments(localMessage2, legacyMessage2);
|
||||
}
|
||||
|
||||
|
@ -232,7 +226,7 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(legacyMessage, viewables, attachments);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
|
||||
|
||||
// Read back all attachments for message and check field values
|
||||
Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
|
||||
|
@ -320,86 +314,38 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
"local-message", accountId, mailboxId, false, true, mProviderContext);
|
||||
|
||||
// Prepare a legacy message with attachments
|
||||
Message legacyMessage = prepareLegacyMessageWithAttachments(2, false, false);
|
||||
Message legacyMessage = prepareLegacyMessageWithAttachments(2, false);
|
||||
|
||||
// Now, convert from legacy to provider and see what happens
|
||||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(legacyMessage, viewables, attachments);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
|
||||
|
||||
// Confirm two attachment objects created
|
||||
Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
|
||||
assertEquals(2, EmailContent.count(mProviderContext, uri, null, null));
|
||||
|
||||
// Now add the attachments again and confirm there are still only two
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
|
||||
assertEquals(2, EmailContent.count(mProviderContext, uri, null, null));
|
||||
|
||||
// Now add a 3rd & 4th attachment and make sure the total is 4, not 2 or 6
|
||||
legacyMessage = prepareLegacyMessageWithAttachments(4, false, false);
|
||||
legacyMessage = prepareLegacyMessageWithAttachments(4, false);
|
||||
viewables = new ArrayList<Part>();
|
||||
attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(legacyMessage, viewables, attachments);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
|
||||
assertEquals(4, EmailContent.count(mProviderContext, uri, null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sunny day test of adding attachments in "local account upgrade" mode
|
||||
*/
|
||||
public void testLocalUpgradeAttachments() throws MessagingException, IOException {
|
||||
// Prepare a local message to add the attachments to
|
||||
final long accountId = 1;
|
||||
final long mailboxId = 1;
|
||||
final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
|
||||
"local-upgrade", accountId, mailboxId, false, true, mProviderContext);
|
||||
|
||||
// Prepare a legacy message with attachments
|
||||
final Message legacyMessage = prepareLegacyMessageWithAttachments(2, true, false);
|
||||
|
||||
// Now, convert from legacy to provider and see what happens
|
||||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(legacyMessage, viewables, attachments);
|
||||
LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, true);
|
||||
|
||||
// Read back all attachments for message and check field values
|
||||
Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
|
||||
Cursor c = mProviderContext.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION,
|
||||
null, null, null);
|
||||
try {
|
||||
assertEquals(2, c.getCount());
|
||||
while (c.moveToNext()) {
|
||||
Attachment attachment = Attachment.getContent(c, Attachment.class);
|
||||
// This attachment should look as if created by modern (provider) MessageCompose.
|
||||
// 1. find the original that it was created from
|
||||
Part fromPart = null;
|
||||
for (Part from : attachments) {
|
||||
String contentType = MimeUtility.unfoldAndDecode(from.getContentType());
|
||||
String name = MimeUtility.getHeaderParameter(contentType, "name");
|
||||
if (name.equals(attachment.mFileName)) {
|
||||
fromPart = from;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue(fromPart != null);
|
||||
// 2. Check values
|
||||
checkAttachment(attachment.mFileName, fromPart, attachment, accountId);
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a legacy message with 1+ attachments
|
||||
* @param numAttachments how many attachments to add
|
||||
* @param localData if true, attachments are "local" data. false = "remote" (from server)
|
||||
* @param filenameInDisposition False: attachment names are sent as content-type:name. True:
|
||||
* attachment names are sent as content-disposition:filename.
|
||||
*/
|
||||
private Message prepareLegacyMessageWithAttachments(int numAttachments, boolean localData,
|
||||
private Message prepareLegacyMessageWithAttachments(int numAttachments,
|
||||
boolean filenameInDisposition) throws MessagingException {
|
||||
BodyPart[] attachmentParts = new BodyPart[numAttachments];
|
||||
for (int i = 0; i < numAttachments; ++i) {
|
||||
|
@ -412,30 +358,18 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
} else {
|
||||
name = ";\n name=" + quotedName;
|
||||
}
|
||||
if (localData) {
|
||||
// generate an attachment that was generated by legacy code (e.g. donut)
|
||||
// for test of upgrading accounts in place
|
||||
// This creator models the code in legacy MessageCompose
|
||||
Uri uri = Uri.parse("content://test/attachment/" + i);
|
||||
MimeBodyPart bp = new MimeBodyPart(
|
||||
new LocalStore.LocalAttachmentBody(uri, mProviderContext));
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/jpg" + name);
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, "attachment" + filename);
|
||||
attachmentParts[i] = bp;
|
||||
} else {
|
||||
// generate an attachment that came from a server
|
||||
BodyPart attachmentPart = MessageTestUtils.bodyPart("image/jpg", null);
|
||||
|
||||
// name=attachmentN size=N00 location=10N
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/jpg" + name);
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
|
||||
"attachment" + filename + ";\n size=" + (i+1) + "00");
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "10" + i);
|
||||
// generate an attachment that came from a server
|
||||
BodyPart attachmentPart = MessageTestUtils.bodyPart("image/jpg", null);
|
||||
|
||||
attachmentParts[i] = attachmentPart;
|
||||
}
|
||||
// name=attachmentN size=N00 location=10N
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/jpg" + name);
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
|
||||
"attachment" + filename + ";\n size=" + (i+1) + "00");
|
||||
attachmentPart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "10" + i);
|
||||
|
||||
attachmentParts[i] = attachmentPart;
|
||||
}
|
||||
|
||||
return prepareLegacyMessageWithAttachments(attachmentParts);
|
||||
|
@ -504,17 +438,8 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
String expectedName = (contentTypeName != null) ? contentTypeName : dispositionFilename;
|
||||
assertEquals(tag, expectedName, actual.mFileName);
|
||||
|
||||
// content URI either both null or both matching
|
||||
String expectedUriString = null;
|
||||
Body body = expected.getBody();
|
||||
if (body instanceof LocalStore.LocalAttachmentBody) {
|
||||
LocalStore.LocalAttachmentBody localBody = (LocalStore.LocalAttachmentBody) body;
|
||||
Uri contentUri = localBody.getContentUri();
|
||||
if (contentUri != null) {
|
||||
expectedUriString = contentUri.toString();
|
||||
}
|
||||
}
|
||||
assertEquals(tag, expectedUriString, actual.mContentUri);
|
||||
// content URI should be null
|
||||
assertNull(tag, actual.mContentUri);
|
||||
|
||||
assertTrue(tag, 0 != actual.mMessageKey);
|
||||
|
||||
|
@ -805,48 +730,4 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
assertEquals(tag + " security", expect.mSecurityFlags, actual.mSecurityFlags);
|
||||
assertEquals(tag + " signature", expect.mSignature, actual.mSignature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test conversion of a legacy mailbox to a provider mailbox
|
||||
*/
|
||||
public void testMakeProviderMailbox() throws MessagingException {
|
||||
EmailContent.Account toAccount = ProviderTestUtils.setupAccount("convert-mailbox",
|
||||
true, mProviderContext);
|
||||
Folder fromFolder = buildTestFolder("INBOX");
|
||||
Mailbox toMailbox = LegacyConversions.makeMailbox(mProviderContext, toAccount, fromFolder);
|
||||
|
||||
// Now test fields in created mailbox
|
||||
assertEquals("INBOX", toMailbox.mDisplayName);
|
||||
assertNull(toMailbox.mServerId);
|
||||
assertNull(toMailbox.mParentServerId);
|
||||
assertEquals(toAccount.mId, toMailbox.mAccountKey);
|
||||
assertEquals(Mailbox.TYPE_INBOX, toMailbox.mType);
|
||||
assertEquals(0, toMailbox.mDelimiter);
|
||||
assertNull(toMailbox.mSyncKey);
|
||||
assertEquals(0, toMailbox.mSyncLookback);
|
||||
assertEquals(0, toMailbox.mSyncInterval);
|
||||
assertEquals(0, toMailbox.mSyncTime);
|
||||
assertTrue(toMailbox.mFlagVisible);
|
||||
assertEquals(0, toMailbox.mFlags);
|
||||
assertEquals(Email.VISIBLE_LIMIT_DEFAULT, toMailbox.mVisibleLimit);
|
||||
assertNull(toMailbox.mSyncStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lightweight Store Folder with simple field population. The folder is "open"
|
||||
* and should be closed by the caller.
|
||||
*/
|
||||
private Folder buildTestFolder(String folderName) throws MessagingException {
|
||||
String localStoreUri =
|
||||
"local://localhost/" + mProviderContext.getDatabasePath(LocalStoreUnitTests.DB_NAME);
|
||||
LocalStore store = (LocalStore) LocalStore.newInstance(localStoreUri, getContext(), null);
|
||||
LocalStore.LocalFolder folder = (LocalStore.LocalFolder) store.getFolder(folderName);
|
||||
folder.open(OpenMode.READ_WRITE, null); // this will create it
|
||||
|
||||
// set a few fields to test values
|
||||
// folder.getName - set by getFolder()
|
||||
folder.setUnreadMessageCount(100);
|
||||
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,19 +16,16 @@
|
|||
|
||||
package com.android.email.mail;
|
||||
|
||||
import com.android.email.mail.internet.BinaryTempFileBody;
|
||||
import com.android.email.mail.internet.MimeBodyPart;
|
||||
import com.android.email.mail.internet.MimeHeader;
|
||||
import com.android.email.mail.internet.MimeMessage;
|
||||
import com.android.email.mail.internet.MimeMultipart;
|
||||
import com.android.email.mail.internet.TextBody;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.provider.AttachmentProvider;
|
||||
import com.android.email.provider.EmailContent;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
|
@ -100,29 +97,6 @@ public class MessageTestUtils {
|
|||
return textPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create attachment BodyPart with content-id.
|
||||
*
|
||||
* @param mimeType MIME type of image body
|
||||
* @param contentId content-id header value (optional - null for no header)
|
||||
* @param attachmentId attachment id of store
|
||||
* @param store LocalStore which stores attachment
|
||||
* @return LocalAttachmentBodyPart with content-id
|
||||
* @throws MessagingException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static BodyPart imagePart(String mimeType, String contentId,
|
||||
long attachmentId, LocalStore store) throws MessagingException, IOException {
|
||||
final BinaryTempFileBody imageBody = new BinaryTempFileBody();
|
||||
final LocalStore.LocalAttachmentBodyPart imagePart =
|
||||
store.new LocalAttachmentBodyPart(imageBody, attachmentId);
|
||||
imagePart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType);
|
||||
if (contentId != null) {
|
||||
imagePart.setHeader(MimeHeader.HEADER_CONTENT_ID, contentId);
|
||||
}
|
||||
return imagePart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for Multipart.
|
||||
*
|
||||
|
|
|
@ -16,24 +16,8 @@
|
|||
|
||||
package com.android.email.mail.internet;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.mail.Message;
|
||||
import com.android.email.mail.MessageTestUtils;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.MessageTestUtils.MessageBuilder;
|
||||
import com.android.email.mail.MessageTestUtils.MultipartBuilder;
|
||||
import com.android.email.mail.MessageTestUtils.TextBuilder;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
import java.io.IOException;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
/**
|
||||
* Tests of the Email HTML utils.
|
||||
|
@ -41,139 +25,13 @@ import java.io.IOException;
|
|||
* You can run this entire test case with:
|
||||
* runtest -c com.android.email.mail.internet.EmailHtmlUtilTest email
|
||||
*/
|
||||
@MediumTest
|
||||
@SmallTest
|
||||
public class EmailHtmlUtilTest extends AndroidTestCase {
|
||||
private EmailContent.Account mAccount;
|
||||
private long mCreatedAccountId = -1;
|
||||
|
||||
private static final String textTags = "<b>Plain</b> &";
|
||||
private static final String textSpaces = "3 spaces end.";
|
||||
private static final String textNewlines = "ab \r\n \n \n\r\n";
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// Force assignment of a default account, and retrieve it
|
||||
Context context = getContext();
|
||||
Email.setTempDirectory(context);
|
||||
|
||||
// Force assignment of a default account
|
||||
long accountId = Account.getDefaultAccountId(context);
|
||||
if (accountId == -1) {
|
||||
Account account = new Account();
|
||||
account.mSenderName = "Bob Sender";
|
||||
account.mEmailAddress = "bob@sender.com";
|
||||
account.save(context);
|
||||
accountId = account.mId;
|
||||
mCreatedAccountId = accountId;
|
||||
}
|
||||
Account.restoreAccountWithId(context, accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
Context context = getContext();
|
||||
// If we created an account, delete it here
|
||||
if (mCreatedAccountId > -1) {
|
||||
context.getContentResolver().delete(
|
||||
ContentUris.withAppendedId(Account.CONTENT_URI, mCreatedAccountId), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for resolving inline image src cid: reference to content uri.
|
||||
*
|
||||
* TODO: These need to be completely rewritten to not use LocalStore messages.
|
||||
*/
|
||||
public void disable_testResolveInlineImage() throws MessagingException, IOException {
|
||||
final LocalStore store = (LocalStore) LocalStore.newInstance(
|
||||
mAccount.getLocalStoreUri(getContext()), mContext, null);
|
||||
// Single cid case.
|
||||
final String cid1 = "cid.1@android.com";
|
||||
final long aid1 = 10;
|
||||
final Uri uri1 = MessageTestUtils.contentUri(aid1, mAccount);
|
||||
final String text1 = new TextBuilder("text1 > ").addCidImg(cid1).build(" <.");
|
||||
final String expected1 = new TextBuilder("text1 > ").addUidImg(uri1).build(" <.");
|
||||
|
||||
// message with cid1
|
||||
final Message msg1 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text1))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpeg", "<"+cid1+">", aid1, store))
|
||||
.build())
|
||||
.build();
|
||||
// Simple case.
|
||||
final String actual1 = EmailHtmlUtil.resolveInlineImage(
|
||||
getContext().getContentResolver(), mAccount.mId, text1, msg1, 0);
|
||||
assertEquals("one content id reference is not resolved",
|
||||
expected1, actual1);
|
||||
|
||||
// Exceed recursive limit.
|
||||
final String actual0 = EmailHtmlUtil.resolveInlineImage(
|
||||
getContext().getContentResolver(), mAccount.mId, text1, msg1, 10);
|
||||
assertEquals("recursive call limit may exceeded",
|
||||
text1, actual0);
|
||||
|
||||
// Multiple cids case.
|
||||
final String cid2 = "cid.2@android.com";
|
||||
final long aid2 = 20;
|
||||
final Uri uri2 = MessageTestUtils.contentUri(aid2, mAccount);
|
||||
final String text2 = new TextBuilder("text2 ").addCidImg(cid2).build(".");
|
||||
final String expected2 = new TextBuilder("text2 ").addUidImg(uri2).build(".");
|
||||
|
||||
// message with only cid2
|
||||
final Message msg2 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text1 + text2))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/gif", cid2, aid2, store))
|
||||
.build())
|
||||
.build();
|
||||
// cid1 is not replaced
|
||||
final String actual2 = EmailHtmlUtil.resolveInlineImage(
|
||||
getContext().getContentResolver(), mAccount.mId, text1 + text2, msg2, 0);
|
||||
assertEquals("only one of two content id is resolved",
|
||||
text1 + expected2, actual2);
|
||||
|
||||
// message with cid1 and cid2
|
||||
final Message msg3 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text2 + text1))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpeg", cid1, aid1, store))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/gif", cid2, aid2, store))
|
||||
.build())
|
||||
.build();
|
||||
// cid1 and cid2 are replaced
|
||||
final String actual3 = EmailHtmlUtil.resolveInlineImage(
|
||||
getContext().getContentResolver(), mAccount.mId, text2 + text1, msg3, 0);
|
||||
assertEquals("two content ids are resolved correctly",
|
||||
expected2 + expected1, actual3);
|
||||
|
||||
// message with many cids and normal attachments
|
||||
final Message msg4 = new MessageBuilder()
|
||||
.setBody(new MultipartBuilder("multipart/mixed")
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpeg", null, 30, store))
|
||||
.addBodyPart(MessageTestUtils.imagePart("application/pdf", cid1, aid1, store))
|
||||
.addBodyPart(new MultipartBuilder("multipart/related")
|
||||
.addBodyPart(MessageTestUtils.textPart("text/html", text2 + text1))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/jpg", cid1, aid1, store))
|
||||
.addBodyPart(MessageTestUtils.imagePart("image/gif", cid2, aid2, store))
|
||||
.buildBodyPart())
|
||||
.addBodyPart(MessageTestUtils.imagePart("application/pdf", cid2, aid2, store))
|
||||
.build())
|
||||
.build();
|
||||
// cid1 and cid2 are replaced
|
||||
final String actual4 = EmailHtmlUtil.resolveInlineImage(
|
||||
getContext().getContentResolver(), mAccount.mId, text2 + text1, msg4, 0);
|
||||
assertEquals("two content ids in deep multipart level are resolved",
|
||||
expected2 + expected1, actual4);
|
||||
|
||||
// No crash on null text
|
||||
final String actual5 = EmailHtmlUtil.resolveInlineImage(getContext().getContentResolver(),
|
||||
mAccount.mId, null, msg4, 0);
|
||||
assertNull(actual5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for escapeCharacterToDisplay in plain text mode.
|
||||
*/
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,7 +19,6 @@ package com.android.email.provider;
|
|||
import com.android.email.AttachmentInfo;
|
||||
import com.android.email.R;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.store.LocalStore;
|
||||
import com.android.email.provider.AttachmentProvider.AttachmentProviderColumns;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.Attachment;
|
||||
|
@ -27,11 +26,9 @@ import com.android.email.provider.EmailContent.Mailbox;
|
|||
import com.android.email.provider.EmailContent.Message;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
|
@ -51,14 +48,6 @@ import java.io.IOException;
|
|||
*/
|
||||
public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvider> {
|
||||
|
||||
/*
|
||||
* This switch will enable us to transition these tests, and the AttachmentProvider, from the
|
||||
* "old" LocalStore model to the "new" provider model. After the transition is complete,
|
||||
* this flag (and its associated code) can be removed.
|
||||
*/
|
||||
private final boolean USE_LOCALSTORE = false;
|
||||
LocalStore mLocalStore = null;
|
||||
|
||||
EmailProvider mEmailProvider;
|
||||
Context mMockContext;
|
||||
ContentResolver mMockResolver;
|
||||
|
@ -81,15 +70,6 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
.addProvider(EmailProvider.EMAIL_AUTHORITY, mEmailProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
|
||||
if (mLocalStore != null) {
|
||||
mLocalStore.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test delete() - should do nothing
|
||||
* test update() - should do nothing
|
||||
|
@ -124,15 +104,10 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
Uri attachment2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
|
||||
Uri attachment3Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id);
|
||||
|
||||
// Test with no attached database - should return null
|
||||
// Test with no attachment found - should return null
|
||||
Cursor c = mMockResolver.query(attachment1Uri, (String[])null, null, (String[])null, null);
|
||||
assertNull(c);
|
||||
|
||||
// Test with an attached database, but no attachment found - should return null
|
||||
setupAttachmentDatabase(account1);
|
||||
c = mMockResolver.query(attachment1Uri, (String[])null, null, (String[])null, null);
|
||||
assertNull(c);
|
||||
|
||||
// Add a couple of attachment entries. Note, query() just uses the DB, and does not
|
||||
// sample the files, so we won't bother creating the files
|
||||
Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1Id, "file1", 100,
|
||||
|
@ -288,15 +263,10 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
|
||||
Uri attachment1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
|
||||
|
||||
// Test with no attached database - should return null
|
||||
// Test with no attachment found - should return null
|
||||
String type = mMockResolver.getType(attachment1Uri);
|
||||
assertNull(type);
|
||||
|
||||
// Test with an attached database, but no attachment found - should return null
|
||||
setupAttachmentDatabase(account1);
|
||||
type = mMockResolver.getType(attachment1Uri);
|
||||
assertNull(type);
|
||||
|
||||
// Add a couple of attachment entries. Note, getType() just uses the DB, and does not
|
||||
// sample the files, so we won't bother creating the files
|
||||
Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file2", 100,
|
||||
|
@ -445,17 +415,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
Uri file1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
|
||||
Uri file2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
|
||||
|
||||
// Test with no attached database - should throw an exception
|
||||
// Test with no attachment found
|
||||
AssetFileDescriptor afd;
|
||||
try {
|
||||
afd = mMockResolver.openAssetFileDescriptor(file1Uri, "r");
|
||||
fail("Should throw an exception on a bad URI");
|
||||
} catch (FileNotFoundException fnf) {
|
||||
// expected
|
||||
}
|
||||
|
||||
// Test with an attached database, but no attachment found
|
||||
setupAttachmentDatabase(account1);
|
||||
try {
|
||||
afd = mMockResolver.openAssetFileDescriptor(file1Uri, "r");
|
||||
fail("Should throw an exception on a missing attachment entry");
|
||||
|
@ -521,13 +482,8 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
Uri thumb2Uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment2Id,
|
||||
62, 62);
|
||||
|
||||
// Test with no attached database - should return null (used to throw SQLiteException)
|
||||
AssetFileDescriptor afd = mMockResolver.openAssetFileDescriptor(thumb1Uri, "r");
|
||||
assertNull(afd);
|
||||
|
||||
// Test with an attached database, but no attachment found
|
||||
setupAttachmentDatabase(account1);
|
||||
afd = mMockResolver.openAssetFileDescriptor(thumb1Uri, "r");
|
||||
AssetFileDescriptor afd = mMockResolver.openAssetFileDescriptor(thumb1Uri, "r");
|
||||
assertNull(afd);
|
||||
|
||||
// Add an attachment (but no associated file)
|
||||
|
@ -584,17 +540,10 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
final long attachment1Id = 1;
|
||||
final Uri attachment1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
|
||||
|
||||
// Test with no attached database - should return input
|
||||
Uri result = AttachmentProvider.resolveAttachmentIdToContentUri(
|
||||
mMockResolver, attachment1Uri);
|
||||
assertEquals(attachment1Uri, result);
|
||||
|
||||
setupAttachmentDatabase(account1);
|
||||
|
||||
// Test with an attached database, but no attachment found - should return input
|
||||
// Test with no attachment found - should return input
|
||||
// We know that the attachmentId 1 does not exist because there are no attachments
|
||||
// created at this point
|
||||
result = AttachmentProvider.resolveAttachmentIdToContentUri(
|
||||
Uri result = AttachmentProvider.resolveAttachmentIdToContentUri(
|
||||
mMockResolver, attachment1Uri);
|
||||
assertEquals(attachment1Uri, result);
|
||||
|
||||
|
@ -786,60 +735,13 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
return outFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the attachments database.
|
||||
*/
|
||||
private void setupAttachmentDatabase(Account forAccount) throws MessagingException {
|
||||
if (USE_LOCALSTORE) {
|
||||
String localStoreUri = "local://localhost/" + dbName(forAccount);
|
||||
mLocalStore = (LocalStore) LocalStore.newInstance(localStoreUri, mMockContext, null);
|
||||
} else {
|
||||
// Nothing to do - EmailProvider is already available for us
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an attachment in the attachments database
|
||||
* @return the id of the attachment just created
|
||||
*/
|
||||
private long addAttachmentToDb(Account forAccount, Attachment newAttachment) {
|
||||
long attachmentId = -1;
|
||||
if (USE_LOCALSTORE) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("message_id", newAttachment.mMessageKey);
|
||||
cv.put("content_uri", newAttachment.mContentUri);
|
||||
cv.put("store_data", (String)null);
|
||||
cv.put("size", newAttachment.mSize);
|
||||
cv.put("name", newAttachment.mFileName);
|
||||
cv.put("mime_type", newAttachment.mMimeType);
|
||||
cv.put("content_id", newAttachment.mContentId);
|
||||
|
||||
SQLiteDatabase db = null;
|
||||
try {
|
||||
db = SQLiteDatabase.openDatabase(dbName(forAccount), null, 0);
|
||||
attachmentId = db.insertOrThrow("attachments", "message_id", cv);
|
||||
}
|
||||
finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newAttachment.save(mMockContext);
|
||||
attachmentId = newAttachment.mId;
|
||||
}
|
||||
return attachmentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the database path+name for a given account
|
||||
*/
|
||||
private String dbName(Account forAccount) {
|
||||
if (USE_LOCALSTORE) {
|
||||
return mMockContext.getDatabasePath(forAccount.mCompatibilityUuid + ".db").toString();
|
||||
} else {
|
||||
throw new java.lang.UnsupportedOperationException();
|
||||
}
|
||||
newAttachment.save(mMockContext);
|
||||
return newAttachment.mId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -847,15 +749,10 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
|
|||
*/
|
||||
private File getAttachmentFile(Account forAccount, long id) {
|
||||
String idString = Long.toString(id);
|
||||
if (USE_LOCALSTORE) {
|
||||
return new File(mMockContext.getDatabasePath(forAccount.mCompatibilityUuid + ".db_att"),
|
||||
idString);
|
||||
} else {
|
||||
File attachmentsDir = mMockContext.getDatabasePath(forAccount.mId + ".db_att");
|
||||
if (!attachmentsDir.exists()) {
|
||||
attachmentsDir.mkdirs();
|
||||
}
|
||||
return new File(attachmentsDir, idString);
|
||||
File attachmentsDir = mMockContext.getDatabasePath(forAccount.mId + ".db_att");
|
||||
if (!attachmentsDir.exists()) {
|
||||
attachmentsDir.mkdirs();
|
||||
}
|
||||
return new File(attachmentsDir, idString);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue