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:
Andy Stadler 2011-02-06 00:54:39 -08:00
parent 5e39f90e9d
commit 7d51b7a05b
16 changed files with 54 additions and 4935 deletions

View File

@ -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"
>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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.
*

View File

@ -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

View File

@ -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);
}
}