2009-05-26 23:40:34 +00:00
|
|
|
/*
|
2009-06-15 21:40:06 +00:00
|
|
|
* Copyright (C) 2009 The Android Open Source Project
|
2009-05-26 23:40:34 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2011-02-10 18:26:56 +00:00
|
|
|
package com.android.emailcommon.provider;
|
2009-05-26 23:40:34 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
import android.content.ContentProviderOperation;
|
|
|
|
import android.content.ContentProviderResult;
|
2009-08-12 10:51:26 +00:00
|
|
|
import android.content.ContentResolver;
|
2009-05-26 23:40:34 +00:00
|
|
|
import android.content.ContentUris;
|
|
|
|
import android.content.ContentValues;
|
|
|
|
import android.content.Context;
|
2009-06-15 21:40:06 +00:00
|
|
|
import android.content.OperationApplicationException;
|
2013-08-21 00:25:59 +00:00
|
|
|
import android.content.res.Resources;
|
2014-01-31 18:48:22 +00:00
|
|
|
import android.database.ContentObservable;
|
2014-01-30 21:23:17 +00:00
|
|
|
import android.database.ContentObserver;
|
2009-05-26 23:40:34 +00:00
|
|
|
import android.database.Cursor;
|
|
|
|
import android.net.Uri;
|
2009-07-15 22:08:53 +00:00
|
|
|
import android.os.Environment;
|
2013-12-04 00:55:48 +00:00
|
|
|
import android.os.Looper;
|
2009-06-15 21:40:06 +00:00
|
|
|
import android.os.Parcel;
|
|
|
|
import android.os.Parcelable;
|
|
|
|
import android.os.RemoteException;
|
2014-04-11 21:42:28 +00:00
|
|
|
import android.provider.BaseColumns;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2011-06-13 22:32:27 +00:00
|
|
|
import com.android.emailcommon.utility.TextUtilities;
|
|
|
|
import com.android.emailcommon.utility.Utility;
|
2013-12-04 00:55:48 +00:00
|
|
|
import com.android.emailcommon.Logging;
|
2013-08-21 00:25:59 +00:00
|
|
|
import com.android.emailcommon.R;
|
2012-06-28 17:40:46 +00:00
|
|
|
import com.android.mail.providers.UIProvider;
|
2013-05-26 04:32:32 +00:00
|
|
|
import com.android.mail.utils.LogUtils;
|
2011-07-01 19:42:41 +00:00
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
2011-06-13 22:32:27 +00:00
|
|
|
|
2014-04-16 21:21:35 +00:00
|
|
|
import org.apache.commons.io.IOUtils;
|
|
|
|
|
2009-07-15 22:08:53 +00:00
|
|
|
import java.io.File;
|
2014-04-16 21:21:35 +00:00
|
|
|
import java.io.IOException;
|
2014-05-08 20:07:54 +00:00
|
|
|
import java.io.InputStream;
|
2014-01-31 18:48:22 +00:00
|
|
|
import java.lang.ref.WeakReference;
|
2009-06-15 21:40:06 +00:00
|
|
|
import java.util.ArrayList;
|
2009-05-26 23:40:34 +00:00
|
|
|
|
2009-07-17 23:29:35 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
/**
|
|
|
|
* EmailContent is the superclass of the various classes of content stored by EmailProvider.
|
2009-07-30 18:41:31 +00:00
|
|
|
*
|
2009-06-15 21:40:06 +00:00
|
|
|
* It is intended to include 1) column definitions for use with the Provider, and 2) convenience
|
|
|
|
* methods for saving and retrieving content from the Provider.
|
2009-07-30 18:41:31 +00:00
|
|
|
*
|
2009-06-15 21:40:06 +00:00
|
|
|
* This class will be used by 1) the Email process (which includes the application and
|
|
|
|
* EmaiLProvider) as well as 2) the Exchange process (which runs independently). It will
|
|
|
|
* necessarily be cloned for use in these two cases.
|
2009-07-30 18:41:31 +00:00
|
|
|
*
|
2009-06-15 21:40:06 +00:00
|
|
|
* Conventions used in naming columns:
|
2014-04-11 21:42:28 +00:00
|
|
|
* BaseColumns._ID is the primary key for all Email records
|
2009-07-30 18:41:31 +00:00
|
|
|
* The SyncColumns interface is used by all classes that are synced to the server directly
|
2009-06-15 21:40:06 +00:00
|
|
|
* (Mailbox and Email)
|
2009-07-30 18:41:31 +00:00
|
|
|
*
|
2009-06-15 21:40:06 +00:00
|
|
|
* <name>_KEY always refers to a foreign key
|
|
|
|
* <name>_ID always refers to a unique identifier (whether on client, server, etc.)
|
|
|
|
*
|
|
|
|
*/
|
2009-05-26 23:40:34 +00:00
|
|
|
public abstract class EmailContent {
|
2012-06-28 17:40:46 +00:00
|
|
|
public static final int NOTIFICATION_MAILBOX_ID_COLUMN = 0;
|
|
|
|
public static final int NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN = 1;
|
2012-12-11 18:37:35 +00:00
|
|
|
public static final int NOTIFICATION_MAILBOX_UNSEEN_COUNT_COLUMN = 2;
|
2012-06-28 17:40:46 +00:00
|
|
|
|
2009-08-12 10:51:26 +00:00
|
|
|
// All classes share this
|
2014-04-11 21:42:28 +00:00
|
|
|
// Use BaseColumns._ID instead
|
|
|
|
@Deprecated
|
2009-08-12 10:51:26 +00:00
|
|
|
public static final String RECORD_ID = "_id";
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] COUNT_COLUMNS = {"count(*)"};
|
2009-08-20 18:09:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This projection can be used with any of the EmailContent classes, when all you need
|
|
|
|
* is a list of id's. Use ID_PROJECTION_COLUMN to access the row data.
|
|
|
|
*/
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] ID_PROJECTION = { BaseColumns._ID };
|
2009-08-20 18:09:39 +00:00
|
|
|
public static final int ID_PROJECTION_COLUMN = 0;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String ID_SELECTION = BaseColumns._ID + " =?";
|
2010-03-05 23:04:11 +00:00
|
|
|
|
2012-06-28 17:40:46 +00:00
|
|
|
public static final int SYNC_STATUS_NONE = UIProvider.SyncStatus.NO_SYNC;
|
|
|
|
public static final int SYNC_STATUS_USER = UIProvider.SyncStatus.USER_REFRESH;
|
|
|
|
public static final int SYNC_STATUS_BACKGROUND = UIProvider.SyncStatus.BACKGROUND_SYNC;
|
2013-10-01 19:02:47 +00:00
|
|
|
public static final int SYNC_STATUS_LIVE = UIProvider.SyncStatus.LIVE_QUERY;
|
2012-06-28 17:40:46 +00:00
|
|
|
|
|
|
|
public static final int LAST_SYNC_RESULT_SUCCESS = UIProvider.LastSyncResult.SUCCESS;
|
|
|
|
public static final int LAST_SYNC_RESULT_AUTH_ERROR = UIProvider.LastSyncResult.AUTH_ERROR;
|
|
|
|
public static final int LAST_SYNC_RESULT_SECURITY_ERROR =
|
|
|
|
UIProvider.LastSyncResult.SECURITY_ERROR;
|
|
|
|
public static final int LAST_SYNC_RESULT_CONNECTION_ERROR =
|
|
|
|
UIProvider.LastSyncResult.CONNECTION_ERROR;
|
|
|
|
public static final int LAST_SYNC_RESULT_INTERNAL_ERROR =
|
|
|
|
UIProvider.LastSyncResult.INTERNAL_ERROR;
|
|
|
|
|
2009-05-26 23:40:34 +00:00
|
|
|
// Newly created objects get this id
|
2010-08-10 00:48:53 +00:00
|
|
|
public static final int NOT_SAVED = -1;
|
2009-05-26 23:40:34 +00:00
|
|
|
// The base Uri that this piece of content came from
|
2009-05-28 18:46:09 +00:00
|
|
|
public Uri mBaseUri;
|
2009-05-26 23:40:34 +00:00
|
|
|
// Lazily initialized uri for this Content
|
2009-05-28 18:46:09 +00:00
|
|
|
private Uri mUri = null;
|
2009-05-26 23:40:34 +00:00
|
|
|
// The id of the Content
|
2009-05-28 18:46:09 +00:00
|
|
|
public long mId = NOT_SAVED;
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2014-01-31 18:48:22 +00:00
|
|
|
/**
|
|
|
|
* Since we close the cursor we use to generate this object, and possibly create the object
|
|
|
|
* without using any cursor at all (eg: parcel), we need to handle observing provider changes
|
|
|
|
* ourselves. This content observer uses a weak reference to keep from rooting this object
|
|
|
|
* in the ContentResolver in case it is not properly disposed of using {@link #close(Context)}
|
|
|
|
*/
|
|
|
|
private SelfContentObserver mSelfObserver;
|
|
|
|
private ContentObservable mObservable;
|
|
|
|
|
2009-05-26 23:40:34 +00:00
|
|
|
// Write the Content into a ContentValues container
|
|
|
|
public abstract ContentValues toContentValues();
|
|
|
|
// Read the Content from a ContentCursor
|
2014-05-08 20:07:54 +00:00
|
|
|
public abstract void restore(Cursor cursor);
|
|
|
|
// Same as above, with the addition of a context to retrieve extra content.
|
|
|
|
// Body uses this to fetch the email body html/text from the provider bypassing the cursor
|
|
|
|
// Not always safe to call on the UI thread.
|
|
|
|
public void restore(Context context, Cursor cursor) {
|
|
|
|
restore(cursor);
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2012-08-23 05:25:42 +00:00
|
|
|
|
|
|
|
public static String EMAIL_PACKAGE_NAME;
|
|
|
|
public static String AUTHORITY;
|
|
|
|
// The notifier authority is used to send notifications regarding changes to messages (insert,
|
|
|
|
// delete, or update) and is intended as an optimization for use by clients of message list
|
|
|
|
// cursors (initially, the email AppWidget).
|
|
|
|
public static String NOTIFIER_AUTHORITY;
|
|
|
|
public static Uri CONTENT_URI;
|
|
|
|
public static final String PARAMETER_LIMIT = "limit";
|
2014-06-20 07:09:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Query parameter for the UI accounts query to enable suppression of the combined account.
|
|
|
|
*/
|
|
|
|
public static final String SUPPRESS_COMBINED_ACCOUNT_PARAM = "suppress_combined";
|
2012-08-23 05:25:42 +00:00
|
|
|
public static Uri CONTENT_NOTIFIER_URI;
|
|
|
|
public static Uri PICK_TRASH_FOLDER_URI;
|
|
|
|
public static Uri PICK_SENT_FOLDER_URI;
|
|
|
|
public static Uri MAILBOX_NOTIFICATION_URI;
|
|
|
|
public static Uri MAILBOX_MOST_RECENT_MESSAGE_URI;
|
|
|
|
public static Uri ACCOUNT_CHECK_URI;
|
2013-08-23 01:22:44 +00:00
|
|
|
|
2013-10-30 02:34:17 +00:00
|
|
|
/**
|
|
|
|
* String for both the EmailProvider call, and the key for the value in the response.
|
|
|
|
* TODO: Eventually this ought to be a device property, not defined by the app.
|
|
|
|
*/
|
|
|
|
public static String DEVICE_FRIENDLY_NAME = "deviceFriendlyName";
|
|
|
|
|
|
|
|
|
2012-08-23 05:25:42 +00:00
|
|
|
public static String PROVIDER_PERMISSION;
|
|
|
|
|
2013-08-17 03:11:54 +00:00
|
|
|
public static synchronized void init(Context context) {
|
2012-08-23 05:25:42 +00:00
|
|
|
if (AUTHORITY == null) {
|
2013-08-21 00:25:59 +00:00
|
|
|
final Resources res = context.getResources();
|
|
|
|
EMAIL_PACKAGE_NAME = res.getString(R.string.email_package_name);
|
2012-08-23 05:25:42 +00:00
|
|
|
AUTHORITY = EMAIL_PACKAGE_NAME + ".provider";
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.d("EmailContent", "init for " + AUTHORITY);
|
2012-08-23 05:25:42 +00:00
|
|
|
NOTIFIER_AUTHORITY = EMAIL_PACKAGE_NAME + ".notifier";
|
|
|
|
CONTENT_URI = Uri.parse("content://" + AUTHORITY);
|
|
|
|
CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY);
|
|
|
|
PICK_TRASH_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickTrashFolder");
|
2012-09-03 20:22:12 +00:00
|
|
|
PICK_SENT_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickSentFolder");
|
2012-08-23 05:25:42 +00:00
|
|
|
MAILBOX_NOTIFICATION_URI = Uri.parse("content://" + AUTHORITY + "/mailboxNotification");
|
|
|
|
MAILBOX_MOST_RECENT_MESSAGE_URI = Uri.parse("content://" + AUTHORITY +
|
|
|
|
"/mailboxMostRecentMessage");
|
|
|
|
ACCOUNT_CHECK_URI = Uri.parse("content://" + AUTHORITY + "/accountCheck");
|
|
|
|
PROVIDER_PERMISSION = EMAIL_PACKAGE_NAME + ".permission.ACCESS_PROVIDER";
|
|
|
|
// Initialize subclasses
|
|
|
|
Account.initAccount();
|
|
|
|
Mailbox.initMailbox();
|
|
|
|
QuickResponse.initQuickResponse();
|
|
|
|
HostAuth.initHostAuth();
|
2013-12-04 00:55:48 +00:00
|
|
|
Credential.initCredential();
|
2012-08-23 05:25:42 +00:00
|
|
|
Policy.initPolicy();
|
|
|
|
Message.initMessage();
|
2013-09-05 20:52:03 +00:00
|
|
|
MessageMove.init();
|
|
|
|
MessageStateChange.init();
|
2012-08-23 05:25:42 +00:00
|
|
|
Body.initBody();
|
|
|
|
Attachment.initAttachment();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-04 00:55:48 +00:00
|
|
|
|
|
|
|
private static void warnIfUiThread() {
|
|
|
|
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
|
2014-01-03 17:07:01 +00:00
|
|
|
LogUtils.w(Logging.LOG_TAG, new Throwable(), "Method called on the UI thread");
|
2013-12-04 00:55:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-04 18:37:11 +00:00
|
|
|
public static boolean isInitialSyncKey(final String syncKey) {
|
|
|
|
return syncKey == null || syncKey.isEmpty() || syncKey.equals("0");
|
|
|
|
}
|
|
|
|
|
2009-05-26 23:40:34 +00:00
|
|
|
// The Uri is lazily initialized
|
|
|
|
public Uri getUri() {
|
2009-06-15 21:40:06 +00:00
|
|
|
if (mUri == null) {
|
2009-05-28 18:46:09 +00:00
|
|
|
mUri = ContentUris.withAppendedId(mBaseUri, mId);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
2009-05-28 18:46:09 +00:00
|
|
|
return mUri;
|
2009-05-26 23:40:34 +00:00
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2009-05-26 23:40:34 +00:00
|
|
|
public boolean isSaved() {
|
2009-05-28 18:46:09 +00:00
|
|
|
return mId != NOT_SAVED;
|
2009-05-26 23:40:34 +00:00
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2011-04-28 00:12:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Restore a subclass of EmailContent from the database
|
|
|
|
* @param context the caller's context
|
|
|
|
* @param klass the class to restore
|
|
|
|
* @param contentUri the content uri of the EmailContent subclass
|
|
|
|
* @param contentProjection the content projection for the EmailContent subclass
|
|
|
|
* @param id the unique id of the object
|
|
|
|
* @return the instantiated object
|
|
|
|
*/
|
|
|
|
public static <T extends EmailContent> T restoreContentWithId(Context context,
|
|
|
|
Class<T> klass, Uri contentUri, String[] contentProjection, long id) {
|
2014-01-30 21:23:17 +00:00
|
|
|
return restoreContentWithId(context, klass, contentUri, contentProjection, id, null);
|
|
|
|
}
|
|
|
|
|
2014-01-31 18:48:22 +00:00
|
|
|
public static <T extends EmailContent> T restoreContentWithId(final Context context,
|
|
|
|
final Class<T> klass, final Uri contentUri, final String[] contentProjection,
|
|
|
|
final long id, final ContentObserver observer) {
|
2013-12-04 00:55:48 +00:00
|
|
|
warnIfUiThread();
|
2014-01-31 18:48:22 +00:00
|
|
|
final Uri u = ContentUris.withAppendedId(contentUri, id);
|
|
|
|
final Cursor c = context.getContentResolver().query(u, contentProjection, null, null, null);
|
2011-05-05 15:29:30 +00:00
|
|
|
if (c == null) throw new ProviderUnavailableException();
|
2011-04-28 00:12:06 +00:00
|
|
|
try {
|
|
|
|
if (c.moveToFirst()) {
|
2014-05-08 20:07:54 +00:00
|
|
|
final T content = getContent(context, c, klass);
|
2014-01-31 18:48:22 +00:00
|
|
|
if (observer != null) {
|
|
|
|
content.registerObserver(context, observer);
|
|
|
|
}
|
|
|
|
return content;
|
2011-04-28 00:12:06 +00:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
c.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-31 18:48:22 +00:00
|
|
|
/**
|
|
|
|
* Register a content observer to be notified when the data underlying this object changes
|
|
|
|
* @param observer ContentObserver to register
|
|
|
|
*/
|
|
|
|
public synchronized void registerObserver(final Context context, final ContentObserver observer) {
|
|
|
|
if (mSelfObserver == null) {
|
|
|
|
mSelfObserver = new SelfContentObserver(this);
|
|
|
|
context.getContentResolver().registerContentObserver(getContentNotificationUri(),
|
|
|
|
true, mSelfObserver);
|
|
|
|
mObservable = new ContentObservable();
|
|
|
|
}
|
|
|
|
mObservable.registerObserver(observer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister a content observer previously registered with
|
|
|
|
* {@link #registerObserver(Context, ContentObserver)}
|
|
|
|
* @param observer ContentObserver to unregister
|
|
|
|
*/
|
|
|
|
public synchronized void unregisterObserver(final ContentObserver observer) {
|
|
|
|
if (mObservable == null) {
|
|
|
|
throw new IllegalStateException("Unregistering with null observable");
|
|
|
|
}
|
|
|
|
mObservable.unregisterObserver(observer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister all content observers previously registered with
|
|
|
|
* {@link #registerObserver(Context, ContentObserver)}
|
|
|
|
*/
|
|
|
|
public synchronized void unregisterAllObservers() {
|
|
|
|
if (mObservable == null) {
|
|
|
|
throw new IllegalStateException("Unregistering with null observable");
|
|
|
|
}
|
|
|
|
mObservable.unregisterAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister all content observers previously registered with
|
|
|
|
* {@link #registerObserver(Context, ContentObserver)} and release internal resources associated
|
|
|
|
* with content observing
|
|
|
|
*/
|
|
|
|
public synchronized void close(final Context context) {
|
|
|
|
if (mSelfObserver == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unregisterAllObservers();
|
|
|
|
context.getContentResolver().unregisterContentObserver(mSelfObserver);
|
|
|
|
mSelfObserver = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a Uri for observing the underlying content. Subclasses that wish to implement content
|
|
|
|
* observing must override this method.
|
|
|
|
* @return Uri for registering content notifications
|
|
|
|
*/
|
|
|
|
protected Uri getContentNotificationUri() {
|
|
|
|
throw new UnsupportedOperationException(
|
|
|
|
"Subclasses must override this method for content observation to work");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method is called when the underlying data has changed, and notifies registered observers
|
|
|
|
* @param selfChange true if this is a self-change notification
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("deprecation")
|
|
|
|
public synchronized void onChange(final boolean selfChange) {
|
|
|
|
if (mObservable != null) {
|
|
|
|
mObservable.dispatchChange(selfChange);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A content observer that calls {@link #onChange(boolean)} when triggered
|
|
|
|
*/
|
|
|
|
private static class SelfContentObserver extends ContentObserver {
|
|
|
|
WeakReference<EmailContent> mContent;
|
|
|
|
|
|
|
|
public SelfContentObserver(final EmailContent content) {
|
|
|
|
super(null);
|
|
|
|
mContent = new WeakReference<EmailContent>(content);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean deliverSelfNotifications() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onChange(final boolean selfChange) {
|
|
|
|
EmailContent content = mContent.get();
|
|
|
|
if (content != null) {
|
|
|
|
content.onChange(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-28 00:12:06 +00:00
|
|
|
|
2009-05-26 23:40:34 +00:00
|
|
|
// The Content sub class must have a no-arg constructor
|
2014-05-08 20:07:54 +00:00
|
|
|
static public <T extends EmailContent> T getContent(final Context context, final Cursor cursor,
|
|
|
|
final Class<T> klass) {
|
2009-05-26 23:40:34 +00:00
|
|
|
try {
|
|
|
|
T content = klass.newInstance();
|
2009-05-28 18:46:09 +00:00
|
|
|
content.mId = cursor.getLong(0);
|
2014-05-08 20:07:54 +00:00
|
|
|
content.restore(context, cursor);
|
2011-02-02 21:23:06 +00:00
|
|
|
return content;
|
2009-05-26 23:40:34 +00:00
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (InstantiationException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2009-05-26 23:40:34 +00:00
|
|
|
public Uri save(Context context) {
|
2009-07-22 22:13:30 +00:00
|
|
|
if (isSaved()) {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
2009-05-28 18:46:09 +00:00
|
|
|
Uri res = context.getContentResolver().insert(mBaseUri, toContentValues());
|
|
|
|
mId = Long.parseLong(res.getPathSegments().get(1));
|
2009-05-26 23:40:34 +00:00
|
|
|
return res;
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2009-05-26 23:40:34 +00:00
|
|
|
public int update(Context context, ContentValues contentValues) {
|
2009-07-22 22:13:30 +00:00
|
|
|
if (!isSaved()) {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
2009-05-26 23:40:34 +00:00
|
|
|
return context.getContentResolver().update(getUri(), contentValues, null, null);
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
static public int update(Context context, Uri baseUri, long id, ContentValues contentValues) {
|
|
|
|
return context.getContentResolver()
|
|
|
|
.update(ContentUris.withAppendedId(baseUri, id), contentValues, null, null);
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2010-08-10 00:48:53 +00:00
|
|
|
static public int delete(Context context, Uri baseUri, long id) {
|
|
|
|
return context.getContentResolver()
|
|
|
|
.delete(ContentUris.withAppendedId(baseUri, id), null, null);
|
|
|
|
}
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
/**
|
|
|
|
* Generic count method that can be used for any ContentProvider
|
2010-08-04 22:38:25 +00:00
|
|
|
*
|
2009-06-15 21:40:06 +00:00
|
|
|
* @param context the calling Context
|
|
|
|
* @param uri the Uri for the provider query
|
|
|
|
* @param selection as with a query call
|
|
|
|
* @param selectionArgs as with a query call
|
|
|
|
* @return the number of items matching the query (or zero)
|
|
|
|
*/
|
2009-06-22 23:13:03 +00:00
|
|
|
static public int count(Context context, Uri uri, String selection, String[] selectionArgs) {
|
2010-07-27 19:52:46 +00:00
|
|
|
return Utility.getFirstRowLong(context,
|
2014-04-11 21:42:28 +00:00
|
|
|
uri, COUNT_COLUMNS, selection, selectionArgs, null, 0, 0L).intValue();
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2010-08-04 22:38:25 +00:00
|
|
|
/**
|
|
|
|
* Same as {@link #count(Context, Uri, String, String[])} without selection.
|
|
|
|
*/
|
|
|
|
static public int count(Context context, Uri uri) {
|
|
|
|
return count(context, uri, null, null);
|
|
|
|
}
|
|
|
|
|
2010-09-28 01:29:50 +00:00
|
|
|
static public Uri uriWithLimit(Uri uri, int limit) {
|
2010-09-29 15:43:31 +00:00
|
|
|
return uri.buildUpon().appendQueryParameter(EmailContent.PARAMETER_LIMIT,
|
2010-09-28 01:29:50 +00:00
|
|
|
Integer.toString(limit)).build();
|
|
|
|
}
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
/**
|
|
|
|
* no public constructor since this is a utility class
|
|
|
|
*/
|
2011-04-28 00:12:06 +00:00
|
|
|
protected EmailContent() {
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public interface SyncColumns {
|
|
|
|
// source id (string) : the source's name of this item
|
|
|
|
public static final String SERVER_ID = "syncServerId";
|
2009-09-23 01:31:10 +00:00
|
|
|
// source's timestamp (long) for this item
|
|
|
|
public static final String SERVER_TIMESTAMP = "syncServerTimeStamp";
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface BodyColumns extends BaseColumns {
|
2009-06-15 21:40:06 +00:00
|
|
|
// Foreign key to the message corresponding to this body
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String MESSAGE_KEY = "messageKey";
|
2014-05-08 20:07:54 +00:00
|
|
|
// The html content itself, not returned on query
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String HTML_CONTENT = "htmlContent";
|
2014-05-08 20:07:54 +00:00
|
|
|
// The html content URI, for ContentResolver#openFileDescriptor()
|
|
|
|
public static final String HTML_CONTENT_URI = "htmlContentUri";
|
|
|
|
// The plain text content itself, not returned on query
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String TEXT_CONTENT = "textContent";
|
2014-05-08 20:07:54 +00:00
|
|
|
// The text content URI, for ContentResolver#openFileDescriptor()
|
|
|
|
public static final String TEXT_CONTENT_URI = "textContentUri";
|
2009-09-02 06:19:12 +00:00
|
|
|
// Replied-to or forwarded body (in html form)
|
2012-06-28 17:40:46 +00:00
|
|
|
@Deprecated
|
2009-09-02 06:19:12 +00:00
|
|
|
public static final String HTML_REPLY = "htmlReply";
|
|
|
|
// Replied-to or forwarded body (in text form)
|
2012-06-28 17:40:46 +00:00
|
|
|
@Deprecated
|
2009-09-02 06:19:12 +00:00
|
|
|
public static final String TEXT_REPLY = "textReply";
|
2009-12-04 20:49:28 +00:00
|
|
|
// A reference to a message's unique id used in reply/forward.
|
|
|
|
// Protocol code can be expected to use this column in determining whether a message can be
|
|
|
|
// deleted safely (i.e. isn't referenced by other messages)
|
2009-09-07 23:03:02 +00:00
|
|
|
public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey";
|
2009-09-23 01:38:28 +00:00
|
|
|
// The text to be placed between a reply/forward response and the original message
|
2012-06-28 17:40:46 +00:00
|
|
|
@Deprecated
|
2009-09-23 01:38:28 +00:00
|
|
|
public static final String INTRO_TEXT = "introText";
|
2012-06-28 17:40:46 +00:00
|
|
|
// The start of quoted text within our text content
|
|
|
|
public static final String QUOTED_TEXT_START_POS = "quotedTextStartPos";
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final class Body extends EmailContent {
|
2009-06-16 19:03:45 +00:00
|
|
|
public static final String TABLE_NAME = "Body";
|
2010-09-10 22:19:57 +00:00
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String SELECTION_BY_MESSAGE_KEY = BodyColumns.MESSAGE_KEY + "=?";
|
2013-10-21 21:59:53 +00:00
|
|
|
|
2012-08-23 05:25:42 +00:00
|
|
|
public static Uri CONTENT_URI;
|
|
|
|
|
|
|
|
public static void initBody() {
|
|
|
|
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
|
|
|
public static final String[] CONTENT_PROJECTION = new String[] {
|
2014-04-25 18:57:00 +00:00
|
|
|
BaseColumns._ID,
|
2014-04-11 21:42:28 +00:00
|
|
|
BodyColumns.MESSAGE_KEY,
|
2014-05-08 20:07:54 +00:00
|
|
|
BodyColumns.HTML_CONTENT_URI,
|
|
|
|
BodyColumns.TEXT_CONTENT_URI,
|
2014-04-11 21:42:28 +00:00
|
|
|
BodyColumns.SOURCE_MESSAGE_KEY,
|
|
|
|
BodyColumns.QUOTED_TEXT_START_POS
|
2009-06-15 21:40:06 +00:00
|
|
|
};
|
|
|
|
|
2014-04-17 21:30:01 +00:00
|
|
|
public static final int CONTENT_ID_COLUMN = 0;
|
|
|
|
public static final int CONTENT_MESSAGE_KEY_COLUMN = 1;
|
2014-05-08 20:07:54 +00:00
|
|
|
public static final int CONTENT_HTML_URI_COLUMN = 2;
|
|
|
|
public static final int CONTENT_TEXT_URI_COLUMN = 3;
|
2014-04-17 21:30:01 +00:00
|
|
|
public static final int CONTENT_SOURCE_KEY_COLUMN = 4;
|
|
|
|
public static final int CONTENT_QUOTED_TEXT_START_POS_COLUMN = 5;
|
|
|
|
|
2010-02-15 13:56:40 +00:00
|
|
|
private static final String[] PROJECTION_SOURCE_KEY =
|
2014-04-25 18:57:00 +00:00
|
|
|
new String[] {BaseColumns._ID, BodyColumns.SOURCE_MESSAGE_KEY};
|
2010-02-15 13:56:40 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
public long mMessageKey;
|
|
|
|
public String mHtmlContent;
|
|
|
|
public String mTextContent;
|
2012-06-28 17:40:46 +00:00
|
|
|
public int mQuotedTextStartPos;
|
2011-07-06 20:48:51 +00:00
|
|
|
|
|
|
|
/**
|
2014-04-11 21:42:28 +00:00
|
|
|
* Points to the ID of the message being replied to or forwarded. Will always be set.
|
2011-07-06 20:48:51 +00:00
|
|
|
*/
|
2009-09-07 23:03:02 +00:00
|
|
|
public long mSourceKey;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2009-06-24 19:48:57 +00:00
|
|
|
public Body() {
|
2009-06-15 21:40:06 +00:00
|
|
|
mBaseUri = CONTENT_URI;
|
|
|
|
}
|
|
|
|
|
2009-09-25 21:54:32 +00:00
|
|
|
@Override
|
2009-06-15 21:40:06 +00:00
|
|
|
public ContentValues toContentValues() {
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
|
|
|
// Assign values for each row.
|
|
|
|
values.put(BodyColumns.MESSAGE_KEY, mMessageKey);
|
|
|
|
values.put(BodyColumns.HTML_CONTENT, mHtmlContent);
|
|
|
|
values.put(BodyColumns.TEXT_CONTENT, mTextContent);
|
2009-09-07 23:03:02 +00:00
|
|
|
values.put(BodyColumns.SOURCE_MESSAGE_KEY, mSourceKey);
|
2009-06-15 21:40:06 +00:00
|
|
|
return values;
|
|
|
|
}
|
|
|
|
|
2011-05-05 15:29:30 +00:00
|
|
|
/**
|
|
|
|
* Given a cursor, restore a Body from it
|
|
|
|
* @param cursor a cursor which must NOT be null
|
|
|
|
* @return the Body as restored from the cursor
|
|
|
|
*/
|
2014-05-08 20:07:54 +00:00
|
|
|
private static Body restoreBodyWithCursor(final Context context, final Cursor cursor) {
|
2009-09-25 21:54:32 +00:00
|
|
|
try {
|
|
|
|
if (cursor.moveToFirst()) {
|
2014-05-08 20:07:54 +00:00
|
|
|
return getContent(context, cursor, Body.class);
|
2009-09-25 21:54:32 +00:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
cursor.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Body restoreBodyWithMessageId(Context context, long messageId) {
|
|
|
|
Cursor c = context.getContentResolver().query(Body.CONTENT_URI,
|
2014-04-11 21:42:28 +00:00
|
|
|
Body.CONTENT_PROJECTION, BodyColumns.MESSAGE_KEY + "=?",
|
2009-09-25 21:54:32 +00:00
|
|
|
new String[] {Long.toString(messageId)}, null);
|
2011-05-05 15:29:30 +00:00
|
|
|
if (c == null) throw new ProviderUnavailableException();
|
2014-05-08 20:07:54 +00:00
|
|
|
return restoreBodyWithCursor(context, c);
|
2009-09-25 21:54:32 +00:00
|
|
|
}
|
2009-07-17 23:29:35 +00:00
|
|
|
|
2009-08-12 10:51:26 +00:00
|
|
|
/**
|
|
|
|
* Returns the bodyId for the given messageId, or -1 if no body is found.
|
|
|
|
*/
|
2010-07-27 19:52:46 +00:00
|
|
|
public static long lookupBodyIdWithMessageId(Context context, long messageId) {
|
|
|
|
return Utility.getFirstRowLong(context, Body.CONTENT_URI,
|
2014-04-11 21:42:28 +00:00
|
|
|
ID_PROJECTION, BodyColumns.MESSAGE_KEY + "=?",
|
|
|
|
new String[] {Long.toString(messageId)}, null, ID_PROJECTION_COLUMN, -1L);
|
2009-08-12 10:51:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the Body for a messageId with the given ContentValues.
|
|
|
|
* If the message has no body, a new body is inserted for the message.
|
|
|
|
* Warning: the argument "values" is modified by this method, setting MESSAGE_KEY.
|
|
|
|
*/
|
2009-08-20 02:07:29 +00:00
|
|
|
public static void updateBodyWithMessageId(Context context, long messageId,
|
2009-08-12 10:51:26 +00:00
|
|
|
ContentValues values) {
|
|
|
|
ContentResolver resolver = context.getContentResolver();
|
2010-07-27 19:52:46 +00:00
|
|
|
long bodyId = lookupBodyIdWithMessageId(context, messageId);
|
2009-08-12 10:51:26 +00:00
|
|
|
values.put(BodyColumns.MESSAGE_KEY, messageId);
|
|
|
|
if (bodyId == -1) {
|
|
|
|
resolver.insert(CONTENT_URI, values);
|
|
|
|
} else {
|
|
|
|
final Uri uri = ContentUris.withAppendedId(CONTENT_URI, bodyId);
|
|
|
|
resolver.update(uri, values, null, null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-01 19:42:41 +00:00
|
|
|
@VisibleForTesting
|
2010-02-15 13:56:40 +00:00
|
|
|
public static long restoreBodySourceKey(Context context, long messageId) {
|
2010-07-27 19:52:46 +00:00
|
|
|
return Utility.getFirstRowLong(context, Body.CONTENT_URI,
|
2010-02-15 13:56:40 +00:00
|
|
|
Body.PROJECTION_SOURCE_KEY,
|
2014-04-11 21:42:28 +00:00
|
|
|
BodyColumns.MESSAGE_KEY + "=?", new String[] {Long.toString(messageId)}, null,
|
|
|
|
0, 0L);
|
2010-02-15 13:56:40 +00:00
|
|
|
}
|
|
|
|
|
2014-05-12 18:33:43 +00:00
|
|
|
public static Uri getBodyTextUriForMessageWithId(long messageId) {
|
|
|
|
return EmailContent.CONTENT_URI.buildUpon()
|
|
|
|
.appendPath("bodyText").appendPath(Long.toString(messageId)).build();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Uri getBodyHtmlUriForMessageWithId(long messageId) {
|
|
|
|
return EmailContent.CONTENT_URI.buildUpon()
|
|
|
|
.appendPath("bodyHtml").appendPath(Long.toString(messageId)).build();
|
|
|
|
}
|
|
|
|
|
2009-07-17 23:29:35 +00:00
|
|
|
public static String restoreBodyTextWithMessageId(Context context, long messageId) {
|
2014-05-12 18:33:43 +00:00
|
|
|
return readBodyFromProvider(context,
|
|
|
|
getBodyTextUriForMessageWithId(messageId).toString());
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2009-07-17 23:29:35 +00:00
|
|
|
public static String restoreBodyHtmlWithMessageId(Context context, long messageId) {
|
2014-05-12 18:33:43 +00:00
|
|
|
return readBodyFromProvider(context,
|
|
|
|
getBodyHtmlUriForMessageWithId(messageId).toString());
|
2009-09-25 21:54:32 +00:00
|
|
|
}
|
|
|
|
|
2014-05-08 20:07:54 +00:00
|
|
|
private static String readBodyFromProvider(final Context context, final String uri) {
|
2014-04-16 21:21:35 +00:00
|
|
|
String content = null;
|
|
|
|
try {
|
2014-05-08 20:07:54 +00:00
|
|
|
final InputStream bodyInput =
|
|
|
|
context.getContentResolver().openInputStream(Uri.parse(uri));
|
2014-04-16 21:21:35 +00:00
|
|
|
try {
|
2014-05-08 20:07:54 +00:00
|
|
|
content = IOUtils.toString(bodyInput);
|
|
|
|
} finally {
|
|
|
|
bodyInput.close();
|
2014-04-16 21:21:35 +00:00
|
|
|
}
|
2014-05-08 20:07:54 +00:00
|
|
|
} catch (final IOException e) {
|
|
|
|
LogUtils.v(LogUtils.TAG, e, "Exception while reading body content");
|
2014-04-16 21:21:35 +00:00
|
|
|
}
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
@Override
|
2014-05-08 20:07:54 +00:00
|
|
|
public void restore(final Cursor cursor) {
|
|
|
|
throw new UnsupportedOperationException("Must have context to restore Body object");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void restore(final Context context, final Cursor cursor) {
|
|
|
|
warnIfUiThread();
|
2009-06-24 19:48:57 +00:00
|
|
|
mBaseUri = EmailContent.Body.CONTENT_URI;
|
2011-02-02 21:23:06 +00:00
|
|
|
mMessageKey = cursor.getLong(CONTENT_MESSAGE_KEY_COLUMN);
|
2014-04-16 21:21:35 +00:00
|
|
|
// These get overwritten below if we find a file descriptor in the respond() call,
|
|
|
|
// but we'll keep this here in case we want to construct a matrix cursor or something
|
|
|
|
// to build a Body object from.
|
2014-05-08 20:07:54 +00:00
|
|
|
mHtmlContent = readBodyFromProvider(context, cursor.getString(CONTENT_HTML_URI_COLUMN));
|
|
|
|
mTextContent = readBodyFromProvider(context, cursor.getString(CONTENT_TEXT_URI_COLUMN));
|
2011-02-02 21:23:06 +00:00
|
|
|
mSourceKey = cursor.getLong(CONTENT_SOURCE_KEY_COLUMN);
|
2012-06-28 17:40:46 +00:00
|
|
|
mQuotedTextStartPos = cursor.getInt(CONTENT_QUOTED_TEXT_START_POS_COLUMN);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface MessageColumns extends BaseColumns, SyncColumns {
|
2009-06-15 21:40:06 +00:00
|
|
|
// Basic columns used in message list presentation
|
|
|
|
// The name as shown to the user in a message list
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String DISPLAY_NAME = "displayName";
|
2009-06-15 21:40:06 +00:00
|
|
|
// The time (millis) as shown to the user in a message list [INDEX]
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String TIMESTAMP = "timeStamp";
|
2009-06-15 21:40:06 +00:00
|
|
|
// Message subject
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String SUBJECT = "subject";
|
2009-06-15 21:40:06 +00:00
|
|
|
// Boolean, unread = 0, read = 1 [INDEX]
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String FLAG_READ = "flagRead";
|
2009-09-10 18:52:36 +00:00
|
|
|
// Load state, see constants below (unloaded, partial, complete, deleted)
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String FLAG_LOADED = "flagLoaded";
|
2009-06-15 21:40:06 +00:00
|
|
|
// Boolean, unflagged = 0, flagged (favorite) = 1
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String FLAG_FAVORITE = "flagFavorite";
|
2009-06-15 21:40:06 +00:00
|
|
|
// Boolean, no attachment = 0, attachment = 1
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String FLAG_ATTACHMENT = "flagAttachment";
|
2009-09-02 06:19:12 +00:00
|
|
|
// Bit field for flags which we'll not be selecting on
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String FLAGS = "flags";
|
2009-06-15 21:40:06 +00:00
|
|
|
|
|
|
|
// Sync related identifiers
|
2012-06-28 17:40:46 +00:00
|
|
|
// Saved draft info (reusing the never-used "clientId" column)
|
|
|
|
public static final String DRAFT_INFO = "clientId";
|
2009-06-15 21:40:06 +00:00
|
|
|
// The message-id in the message's header
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String MESSAGE_ID = "messageId";
|
2009-06-15 21:40:06 +00:00
|
|
|
|
|
|
|
// References to other Email objects in the database
|
|
|
|
// Foreign key to the Mailbox holding this message [INDEX]
|
Add an additional mailbox key column to message table
b/11294681
The problem is that when we try to open an attachment for a
message in search results, it fails. The reason is that part of
loading the attachment, we need to open the remote folder the
message is in. For search results, the message's mailboxKey is
the special fake "search_results" folder, which doesn't actually
exist on the server.
For this change, I've added a new column called "mainMailboxKey".
For search results, this column will be populated with the real
mailbox the message is in. It will be blank for other messages.
This is a quick and low risk fix for this bug, but it's kind
of awkward. We would prefer to do one or both of the following
some time after MR1.
1. Make the "search_results" folder be a virtual folder, the same
way that unread, starred, and other virtual folders are. For these,
there is actually no mailbox row in the database, just some
queries that check various flags in the messages and behave
like folders in the UI. The messages actually still reside in the
real folders.
2. Remove the requirement to open the folder at all to load the
attachment.
Change-Id: I825ab846f78bf8b041a5d1d579260dc5d7b4c522
2013-10-23 18:18:54 +00:00
|
|
|
// TODO: This column is used in a complicated way: Usually, this refers to the mailbox
|
|
|
|
// the server considers this message to be in. In the case of search results, this key
|
|
|
|
// will refer to a special "search" mailbox, which does not exist on the server.
|
|
|
|
// This is confusing and causes problems, see b/11294681.
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String MAILBOX_KEY = "mailboxKey";
|
2009-06-15 21:40:06 +00:00
|
|
|
// Foreign key to the Account holding this message
|
2009-07-30 18:41:31 +00:00
|
|
|
public static final String ACCOUNT_KEY = "accountKey";
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2009-07-09 21:46:03 +00:00
|
|
|
// Address lists, packed with Address.pack()
|
2009-06-15 21:40:06 +00:00
|
|
|
public static final String FROM_LIST = "fromList";
|
|
|
|
public static final String TO_LIST = "toList";
|
|
|
|
public static final String CC_LIST = "ccList";
|
|
|
|
public static final String BCC_LIST = "bccList";
|
|
|
|
public static final String REPLY_TO_LIST = "replyToList";
|
2010-02-16 00:01:38 +00:00
|
|
|
// Meeting invitation related information (for now, start time in ms)
|
|
|
|
public static final String MEETING_INFO = "meetingInfo";
|
2010-09-02 02:06:15 +00:00
|
|
|
// A text "snippet" derived from the body of the message
|
|
|
|
public static final String SNIPPET = "snippet";
|
2011-06-23 17:52:21 +00:00
|
|
|
// A column that can be used by sync adapters to store search-related information about
|
|
|
|
// a retrieved message (the messageKey for search results will be a TYPE_SEARCH mailbox
|
|
|
|
// and the sync adapter might, for example, need more information about the original source
|
|
|
|
// of the message)
|
|
|
|
public static final String PROTOCOL_SEARCH_INFO = "protocolSearchInfo";
|
2012-06-28 17:40:46 +00:00
|
|
|
// Simple thread topic
|
|
|
|
public static final String THREAD_TOPIC = "threadTopic";
|
2012-06-29 16:42:05 +00:00
|
|
|
// For sync adapter use
|
|
|
|
public static final String SYNC_DATA = "syncData";
|
2012-12-11 18:37:35 +00:00
|
|
|
|
|
|
|
/** Boolean, unseen = 0, seen = 1 [INDEX] */
|
|
|
|
public static final String FLAG_SEEN = "flagSeen";
|
Add an additional mailbox key column to message table
b/11294681
The problem is that when we try to open an attachment for a
message in search results, it fails. The reason is that part of
loading the attachment, we need to open the remote folder the
message is in. For search results, the message's mailboxKey is
the special fake "search_results" folder, which doesn't actually
exist on the server.
For this change, I've added a new column called "mainMailboxKey".
For search results, this column will be populated with the real
mailbox the message is in. It will be blank for other messages.
This is a quick and low risk fix for this bug, but it's kind
of awkward. We would prefer to do one or both of the following
some time after MR1.
1. Make the "search_results" folder be a virtual folder, the same
way that unread, starred, and other virtual folders are. For these,
there is actually no mailbox row in the database, just some
queries that check various flags in the messages and behave
like folders in the UI. The messages actually still reside in the
real folders.
2. Remove the requirement to open the folder at all to load the
attachment.
Change-Id: I825ab846f78bf8b041a5d1d579260dc5d7b4c522
2013-10-23 18:18:54 +00:00
|
|
|
|
|
|
|
// References to other Email objects in the database
|
|
|
|
// Foreign key to the Mailbox holding this message [INDEX]
|
|
|
|
// In cases where mailboxKey is NOT the real mailbox the server considers this message in,
|
|
|
|
// this will be set. See b/11294681
|
|
|
|
// We'd like to get rid of this column when the other changes mentioned in that bug
|
|
|
|
// can be addressed.
|
|
|
|
public static final String MAIN_MAILBOX_KEY = "mainMailboxKey";
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final class Message extends EmailContent {
|
2013-03-13 00:45:11 +00:00
|
|
|
private static final String LOG_TAG = "Email";
|
|
|
|
|
2009-06-16 19:03:45 +00:00
|
|
|
public static final String TABLE_NAME = "Message";
|
2009-06-27 19:14:14 +00:00
|
|
|
public static final String UPDATED_TABLE_NAME = "Message_Updates";
|
|
|
|
public static final String DELETED_TABLE_NAME = "Message_Deletes";
|
|
|
|
|
|
|
|
// To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id)
|
2012-08-23 05:25:42 +00:00
|
|
|
public static Uri CONTENT_URI;
|
|
|
|
public static Uri CONTENT_URI_LIMIT_1;
|
|
|
|
public static Uri SYNCED_CONTENT_URI;
|
|
|
|
public static Uri SELECTED_MESSAGE_CONTENT_URI ;
|
|
|
|
public static Uri DELETED_CONTENT_URI;
|
|
|
|
public static Uri UPDATED_CONTENT_URI;
|
|
|
|
public static Uri NOTIFIER_URI;
|
|
|
|
|
|
|
|
public static void initMessage() {
|
|
|
|
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
|
|
|
|
CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
|
|
|
|
SYNCED_CONTENT_URI =
|
|
|
|
Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
|
|
|
|
SELECTED_MESSAGE_CONTENT_URI =
|
|
|
|
Uri.parse(EmailContent.CONTENT_URI + "/messageBySelection");
|
|
|
|
DELETED_CONTENT_URI =
|
|
|
|
Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
|
|
|
|
UPDATED_CONTENT_URI =
|
|
|
|
Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
|
|
|
|
NOTIFIER_URI =
|
|
|
|
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message");
|
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
|
|
|
|
public static final int CONTENT_ID_COLUMN = 0;
|
|
|
|
public static final int CONTENT_DISPLAY_NAME_COLUMN = 1;
|
|
|
|
public static final int CONTENT_TIMESTAMP_COLUMN = 2;
|
|
|
|
public static final int CONTENT_SUBJECT_COLUMN = 3;
|
2009-09-02 06:19:12 +00:00
|
|
|
public static final int CONTENT_FLAG_READ_COLUMN = 4;
|
|
|
|
public static final int CONTENT_FLAG_LOADED_COLUMN = 5;
|
|
|
|
public static final int CONTENT_FLAG_FAVORITE_COLUMN = 6;
|
|
|
|
public static final int CONTENT_FLAG_ATTACHMENT_COLUMN = 7;
|
|
|
|
public static final int CONTENT_FLAGS_COLUMN = 8;
|
|
|
|
public static final int CONTENT_SERVER_ID_COLUMN = 9;
|
2012-06-28 17:40:46 +00:00
|
|
|
public static final int CONTENT_DRAFT_INFO_COLUMN = 10;
|
2009-09-02 06:19:12 +00:00
|
|
|
public static final int CONTENT_MESSAGE_ID_COLUMN = 11;
|
|
|
|
public static final int CONTENT_MAILBOX_KEY_COLUMN = 12;
|
|
|
|
public static final int CONTENT_ACCOUNT_KEY_COLUMN = 13;
|
|
|
|
public static final int CONTENT_FROM_LIST_COLUMN = 14;
|
|
|
|
public static final int CONTENT_TO_LIST_COLUMN = 15;
|
|
|
|
public static final int CONTENT_CC_LIST_COLUMN = 16;
|
|
|
|
public static final int CONTENT_BCC_LIST_COLUMN = 17;
|
|
|
|
public static final int CONTENT_REPLY_TO_COLUMN = 18;
|
2009-09-23 01:31:10 +00:00
|
|
|
public static final int CONTENT_SERVER_TIMESTAMP_COLUMN = 19;
|
2010-02-16 00:01:38 +00:00
|
|
|
public static final int CONTENT_MEETING_INFO_COLUMN = 20;
|
2010-09-02 02:06:15 +00:00
|
|
|
public static final int CONTENT_SNIPPET_COLUMN = 21;
|
2011-06-23 17:52:21 +00:00
|
|
|
public static final int CONTENT_PROTOCOL_SEARCH_INFO_COLUMN = 22;
|
2012-06-28 17:40:46 +00:00
|
|
|
public static final int CONTENT_THREAD_TOPIC_COLUMN = 23;
|
2012-06-29 16:42:05 +00:00
|
|
|
public static final int CONTENT_SYNC_DATA_COLUMN = 24;
|
2012-12-11 18:37:35 +00:00
|
|
|
public static final int CONTENT_FLAG_SEEN_COLUMN = 25;
|
Add an additional mailbox key column to message table
b/11294681
The problem is that when we try to open an attachment for a
message in search results, it fails. The reason is that part of
loading the attachment, we need to open the remote folder the
message is in. For search results, the message's mailboxKey is
the special fake "search_results" folder, which doesn't actually
exist on the server.
For this change, I've added a new column called "mainMailboxKey".
For search results, this column will be populated with the real
mailbox the message is in. It will be blank for other messages.
This is a quick and low risk fix for this bug, but it's kind
of awkward. We would prefer to do one or both of the following
some time after MR1.
1. Make the "search_results" folder be a virtual folder, the same
way that unread, starred, and other virtual folders are. For these,
there is actually no mailbox row in the database, just some
queries that check various flags in the messages and behave
like folders in the UI. The messages actually still reside in the
real folders.
2. Remove the requirement to open the folder at all to load the
attachment.
Change-Id: I825ab846f78bf8b041a5d1d579260dc5d7b4c522
2013-10-23 18:18:54 +00:00
|
|
|
public static final int CONTENT_MAIN_MAILBOX_KEY_COLUMN = 26;
|
2009-09-02 06:19:12 +00:00
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] CONTENT_PROJECTION = {
|
|
|
|
MessageColumns._ID,
|
2009-09-02 06:19:12 +00:00
|
|
|
MessageColumns.DISPLAY_NAME, MessageColumns.TIMESTAMP,
|
|
|
|
MessageColumns.SUBJECT, MessageColumns.FLAG_READ,
|
2009-06-15 21:40:06 +00:00
|
|
|
MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE,
|
2009-09-02 06:19:12 +00:00
|
|
|
MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS,
|
2012-06-28 17:40:46 +00:00
|
|
|
SyncColumns.SERVER_ID, MessageColumns.DRAFT_INFO,
|
2009-09-02 06:19:12 +00:00
|
|
|
MessageColumns.MESSAGE_ID, MessageColumns.MAILBOX_KEY,
|
|
|
|
MessageColumns.ACCOUNT_KEY, MessageColumns.FROM_LIST,
|
|
|
|
MessageColumns.TO_LIST, MessageColumns.CC_LIST,
|
|
|
|
MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST,
|
2010-09-02 02:06:15 +00:00
|
|
|
SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO,
|
2012-06-28 17:40:46 +00:00
|
|
|
MessageColumns.SNIPPET, MessageColumns.PROTOCOL_SEARCH_INFO,
|
Add an additional mailbox key column to message table
b/11294681
The problem is that when we try to open an attachment for a
message in search results, it fails. The reason is that part of
loading the attachment, we need to open the remote folder the
message is in. For search results, the message's mailboxKey is
the special fake "search_results" folder, which doesn't actually
exist on the server.
For this change, I've added a new column called "mainMailboxKey".
For search results, this column will be populated with the real
mailbox the message is in. It will be blank for other messages.
This is a quick and low risk fix for this bug, but it's kind
of awkward. We would prefer to do one or both of the following
some time after MR1.
1. Make the "search_results" folder be a virtual folder, the same
way that unread, starred, and other virtual folders are. For these,
there is actually no mailbox row in the database, just some
queries that check various flags in the messages and behave
like folders in the UI. The messages actually still reside in the
real folders.
2. Remove the requirement to open the folder at all to load the
attachment.
Change-Id: I825ab846f78bf8b041a5d1d579260dc5d7b4c522
2013-10-23 18:18:54 +00:00
|
|
|
MessageColumns.THREAD_TOPIC, MessageColumns.SYNC_DATA,
|
|
|
|
MessageColumns.FLAG_SEEN, MessageColumns.MAIN_MAILBOX_KEY
|
2009-06-15 21:40:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
public static final int LIST_ID_COLUMN = 0;
|
|
|
|
public static final int LIST_DISPLAY_NAME_COLUMN = 1;
|
|
|
|
public static final int LIST_TIMESTAMP_COLUMN = 2;
|
|
|
|
public static final int LIST_SUBJECT_COLUMN = 3;
|
2009-09-02 06:19:12 +00:00
|
|
|
public static final int LIST_READ_COLUMN = 4;
|
|
|
|
public static final int LIST_LOADED_COLUMN = 5;
|
|
|
|
public static final int LIST_FAVORITE_COLUMN = 6;
|
|
|
|
public static final int LIST_ATTACHMENT_COLUMN = 7;
|
|
|
|
public static final int LIST_FLAGS_COLUMN = 8;
|
|
|
|
public static final int LIST_MAILBOX_KEY_COLUMN = 9;
|
|
|
|
public static final int LIST_ACCOUNT_KEY_COLUMN = 10;
|
|
|
|
public static final int LIST_SERVER_ID_COLUMN = 11;
|
2010-09-02 02:06:15 +00:00
|
|
|
public static final int LIST_SNIPPET_COLUMN = 12;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
|
|
|
// Public projection for common list columns
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] LIST_PROJECTION = {
|
|
|
|
MessageColumns._ID,
|
2009-09-02 06:19:12 +00:00
|
|
|
MessageColumns.DISPLAY_NAME, MessageColumns.TIMESTAMP,
|
|
|
|
MessageColumns.SUBJECT, MessageColumns.FLAG_READ,
|
2009-06-15 21:40:06 +00:00
|
|
|
MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE,
|
2009-09-02 06:19:12 +00:00
|
|
|
MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS,
|
|
|
|
MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY,
|
2010-09-02 02:06:15 +00:00
|
|
|
SyncColumns.SERVER_ID, MessageColumns.SNIPPET
|
2009-06-15 21:40:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
public static final int ID_COLUMNS_ID_COLUMN = 0;
|
|
|
|
public static final int ID_COLUMNS_SYNC_SERVER_ID = 1;
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] ID_COLUMNS_PROJECTION = {
|
|
|
|
MessageColumns._ID, SyncColumns.SERVER_ID
|
2009-06-15 21:40:06 +00:00
|
|
|
};
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] ID_COLUMN_PROJECTION = { MessageColumns._ID };
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2013-08-23 01:22:44 +00:00
|
|
|
public static final String ACCOUNT_KEY_SELECTION =
|
2011-01-20 20:39:13 +00:00
|
|
|
MessageColumns.ACCOUNT_KEY + "=?";
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] MAILBOX_KEY_PROJECTION = { MessageColumns.MAILBOX_KEY };
|
2013-09-12 22:23:54 +00:00
|
|
|
|
2011-05-13 21:09:22 +00:00
|
|
|
/**
|
|
|
|
* Selection for messages that are loaded
|
|
|
|
*
|
|
|
|
* POP messages at the initial stage have very little information. (Server UID only)
|
|
|
|
* Use this to make sure they're not visible on any UI.
|
|
|
|
* This means unread counts on the mailbox list can be different from the
|
|
|
|
* number of messages in the message list, but it should be transient...
|
|
|
|
*/
|
2011-01-24 22:11:20 +00:00
|
|
|
public static final String FLAG_LOADED_SELECTION =
|
|
|
|
MessageColumns.FLAG_LOADED + " IN ("
|
|
|
|
+ Message.FLAG_LOADED_PARTIAL + "," + Message.FLAG_LOADED_COMPLETE
|
|
|
|
+ ")";
|
|
|
|
|
2011-01-13 00:38:25 +00:00
|
|
|
public static final String ALL_FAVORITE_SELECTION =
|
|
|
|
MessageColumns.FLAG_FAVORITE + "=1 AND "
|
|
|
|
+ MessageColumns.MAILBOX_KEY + " NOT IN ("
|
2014-04-11 21:42:28 +00:00
|
|
|
+ "SELECT " + MailboxColumns._ID + " FROM " + Mailbox.TABLE_NAME + ""
|
2011-01-13 00:38:25 +00:00
|
|
|
+ " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_TRASH
|
2011-01-24 22:11:20 +00:00
|
|
|
+ ")"
|
|
|
|
+ " AND " + FLAG_LOADED_SELECTION;
|
2011-05-13 21:09:22 +00:00
|
|
|
|
2011-01-20 20:39:13 +00:00
|
|
|
/** Selection to retrieve all messages in "inbox" for any account */
|
2011-05-13 21:09:22 +00:00
|
|
|
public static final String ALL_INBOX_SELECTION =
|
2011-01-20 20:39:13 +00:00
|
|
|
MessageColumns.MAILBOX_KEY + " IN ("
|
2014-04-11 21:42:28 +00:00
|
|
|
+ "SELECT " + MailboxColumns._ID + " FROM " + Mailbox.TABLE_NAME
|
2011-01-20 20:39:13 +00:00
|
|
|
+ " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX
|
2011-01-24 22:11:20 +00:00
|
|
|
+ ")"
|
|
|
|
+ " AND " + FLAG_LOADED_SELECTION;
|
2011-05-13 21:09:22 +00:00
|
|
|
|
|
|
|
/** Selection to retrieve all messages in "drafts" for any account */
|
|
|
|
public static final String ALL_DRAFT_SELECTION =
|
|
|
|
MessageColumns.MAILBOX_KEY + " IN ("
|
2014-04-11 21:42:28 +00:00
|
|
|
+ "SELECT " + MailboxColumns._ID + " FROM " + Mailbox.TABLE_NAME
|
2011-05-13 21:09:22 +00:00
|
|
|
+ " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_DRAFTS
|
|
|
|
+ ")"
|
|
|
|
+ " AND " + FLAG_LOADED_SELECTION;
|
|
|
|
|
|
|
|
/** Selection to retrieve all messages in "outbox" for any account */
|
|
|
|
public static final String ALL_OUTBOX_SELECTION =
|
|
|
|
MessageColumns.MAILBOX_KEY + " IN ("
|
2014-04-11 21:42:28 +00:00
|
|
|
+ "SELECT " + MailboxColumns._ID + " FROM " + Mailbox.TABLE_NAME
|
2011-05-13 21:09:22 +00:00
|
|
|
+ " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_OUTBOX
|
|
|
|
+ ")"; // NOTE No flag_loaded test for outboxes.
|
|
|
|
|
2011-01-20 20:39:13 +00:00
|
|
|
/** Selection to retrieve unread messages in "inbox" for any account */
|
2011-05-13 21:09:22 +00:00
|
|
|
public static final String ALL_UNREAD_SELECTION =
|
|
|
|
MessageColumns.FLAG_READ + "=0 AND " + ALL_INBOX_SELECTION;
|
|
|
|
|
2011-06-01 17:31:21 +00:00
|
|
|
/** Selection to retrieve unread messages in "inbox" for one account */
|
|
|
|
public static final String PER_ACCOUNT_UNREAD_SELECTION =
|
2011-06-01 19:29:10 +00:00
|
|
|
ACCOUNT_KEY_SELECTION + " AND " + ALL_UNREAD_SELECTION;
|
2011-06-01 17:31:21 +00:00
|
|
|
|
2011-01-20 20:39:13 +00:00
|
|
|
/** Selection to retrieve all messages in "inbox" for one account */
|
|
|
|
public static final String PER_ACCOUNT_INBOX_SELECTION =
|
2011-05-13 21:09:22 +00:00
|
|
|
ACCOUNT_KEY_SELECTION + " AND " + ALL_INBOX_SELECTION;
|
2010-09-26 23:16:21 +00:00
|
|
|
|
2011-06-01 19:29:10 +00:00
|
|
|
public static final String PER_ACCOUNT_FAVORITE_SELECTION =
|
2011-01-13 00:38:25 +00:00
|
|
|
ACCOUNT_KEY_SELECTION + " AND " + ALL_FAVORITE_SELECTION;
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String MAILBOX_SELECTION = MessageColumns.MAILBOX_KEY + "=?";
|
2013-09-19 15:22:12 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
// _id field is in AbstractContent
|
|
|
|
public String mDisplayName;
|
|
|
|
public long mTimeStamp;
|
|
|
|
public String mSubject;
|
|
|
|
public boolean mFlagRead = false;
|
2012-12-11 18:37:35 +00:00
|
|
|
public boolean mFlagSeen = false;
|
2009-09-10 18:52:36 +00:00
|
|
|
public int mFlagLoaded = FLAG_LOADED_UNLOADED;
|
2009-06-15 21:40:06 +00:00
|
|
|
public boolean mFlagFavorite = false;
|
|
|
|
public boolean mFlagAttachment = false;
|
|
|
|
public int mFlags = 0;
|
|
|
|
|
|
|
|
public String mServerId;
|
2009-09-23 01:31:10 +00:00
|
|
|
public long mServerTimeStamp;
|
2012-06-28 17:40:46 +00:00
|
|
|
public int mDraftInfo;
|
2009-06-15 21:40:06 +00:00
|
|
|
public String mMessageId;
|
|
|
|
|
|
|
|
public long mMailboxKey;
|
|
|
|
public long mAccountKey;
|
Add an additional mailbox key column to message table
b/11294681
The problem is that when we try to open an attachment for a
message in search results, it fails. The reason is that part of
loading the attachment, we need to open the remote folder the
message is in. For search results, the message's mailboxKey is
the special fake "search_results" folder, which doesn't actually
exist on the server.
For this change, I've added a new column called "mainMailboxKey".
For search results, this column will be populated with the real
mailbox the message is in. It will be blank for other messages.
This is a quick and low risk fix for this bug, but it's kind
of awkward. We would prefer to do one or both of the following
some time after MR1.
1. Make the "search_results" folder be a virtual folder, the same
way that unread, starred, and other virtual folders are. For these,
there is actually no mailbox row in the database, just some
queries that check various flags in the messages and behave
like folders in the UI. The messages actually still reside in the
real folders.
2. Remove the requirement to open the folder at all to load the
attachment.
Change-Id: I825ab846f78bf8b041a5d1d579260dc5d7b4c522
2013-10-23 18:18:54 +00:00
|
|
|
public long mMainMailboxKey;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
|
|
|
public String mFrom;
|
|
|
|
public String mTo;
|
|
|
|
public String mCc;
|
|
|
|
public String mBcc;
|
|
|
|
public String mReplyTo;
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2010-02-16 00:01:38 +00:00
|
|
|
// For now, just the start time of a meeting invite, in ms
|
|
|
|
public String mMeetingInfo;
|
|
|
|
|
2010-09-02 02:06:15 +00:00
|
|
|
public String mSnippet;
|
|
|
|
|
2011-06-23 17:52:21 +00:00
|
|
|
public String mProtocolSearchInfo;
|
|
|
|
|
2012-06-28 17:40:46 +00:00
|
|
|
public String mThreadTopic;
|
|
|
|
|
2012-06-29 16:42:05 +00:00
|
|
|
public String mSyncData;
|
|
|
|
|
2011-08-11 00:52:34 +00:00
|
|
|
/**
|
|
|
|
* Base64-encoded representation of the byte array provided by servers for identifying
|
|
|
|
* messages belonging to the same conversation thread. Currently unsupported and not
|
|
|
|
* persisted in the database.
|
|
|
|
*/
|
|
|
|
public String mServerConversationId;
|
|
|
|
|
2009-12-04 20:49:28 +00:00
|
|
|
// The following transient members may be used while building and manipulating messages,
|
2011-07-06 20:48:51 +00:00
|
|
|
// but they are NOT persisted directly by EmailProvider. See Body for related fields.
|
2009-06-15 21:40:06 +00:00
|
|
|
transient public String mText;
|
|
|
|
transient public String mHtml;
|
2009-09-07 23:03:02 +00:00
|
|
|
transient public long mSourceKey;
|
2009-06-15 21:40:06 +00:00
|
|
|
transient public ArrayList<Attachment> mAttachments = null;
|
2012-06-28 17:40:46 +00:00
|
|
|
transient public int mQuotedTextStartPos;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2011-06-23 17:52:21 +00:00
|
|
|
|
2009-09-07 23:03:02 +00:00
|
|
|
// Values used in mFlagRead
|
2009-06-15 21:40:06 +00:00
|
|
|
public static final int UNREAD = 0;
|
|
|
|
public static final int READ = 1;
|
|
|
|
|
2009-09-07 23:03:02 +00:00
|
|
|
// Values used in mFlagLoaded
|
2009-09-10 18:52:36 +00:00
|
|
|
public static final int FLAG_LOADED_UNLOADED = 0;
|
|
|
|
public static final int FLAG_LOADED_COMPLETE = 1;
|
|
|
|
public static final int FLAG_LOADED_PARTIAL = 2;
|
|
|
|
public static final int FLAG_LOADED_DELETED = 3;
|
2012-08-02 17:53:40 +00:00
|
|
|
public static final int FLAG_LOADED_UNKNOWN = 4;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2009-09-07 23:03:02 +00:00
|
|
|
// Bits used in mFlags
|
2010-01-25 20:38:32 +00:00
|
|
|
// The following three states are mutually exclusive, and indicate whether the message is an
|
2009-09-07 23:03:02 +00:00
|
|
|
// original, a reply, or a forward
|
|
|
|
public static final int FLAG_TYPE_REPLY = 1<<0;
|
|
|
|
public static final int FLAG_TYPE_FORWARD = 1<<1;
|
|
|
|
public static final int FLAG_TYPE_MASK = FLAG_TYPE_REPLY | FLAG_TYPE_FORWARD;
|
2010-02-22 20:57:33 +00:00
|
|
|
// The following flags indicate messages that are determined to be incoming meeting related
|
|
|
|
// (e.g. invites from others)
|
|
|
|
public static final int FLAG_INCOMING_MEETING_INVITE = 1<<2;
|
|
|
|
public static final int FLAG_INCOMING_MEETING_CANCEL = 1<<3;
|
|
|
|
public static final int FLAG_INCOMING_MEETING_MASK =
|
|
|
|
FLAG_INCOMING_MEETING_INVITE | FLAG_INCOMING_MEETING_CANCEL;
|
|
|
|
// The following flags indicate messages that are outgoing and meeting related
|
|
|
|
// (e.g. invites TO others)
|
|
|
|
public static final int FLAG_OUTGOING_MEETING_INVITE = 1<<4;
|
|
|
|
public static final int FLAG_OUTGOING_MEETING_CANCEL = 1<<5;
|
|
|
|
public static final int FLAG_OUTGOING_MEETING_ACCEPT = 1<<6;
|
|
|
|
public static final int FLAG_OUTGOING_MEETING_DECLINE = 1<<7;
|
|
|
|
public static final int FLAG_OUTGOING_MEETING_TENTATIVE = 1<<8;
|
|
|
|
public static final int FLAG_OUTGOING_MEETING_MASK =
|
|
|
|
FLAG_OUTGOING_MEETING_INVITE | FLAG_OUTGOING_MEETING_CANCEL |
|
|
|
|
FLAG_OUTGOING_MEETING_ACCEPT | FLAG_OUTGOING_MEETING_DECLINE |
|
|
|
|
FLAG_OUTGOING_MEETING_TENTATIVE;
|
2010-02-26 21:13:34 +00:00
|
|
|
public static final int FLAG_OUTGOING_MEETING_REQUEST_MASK =
|
|
|
|
FLAG_OUTGOING_MEETING_INVITE | FLAG_OUTGOING_MEETING_CANCEL;
|
2010-10-06 19:55:29 +00:00
|
|
|
// 8 general purpose flags (bits) that may be used at the discretion of the sync adapter
|
|
|
|
public static final int FLAG_SYNC_ADAPTER_SHIFT = 9;
|
|
|
|
public static final int FLAG_SYNC_ADAPTER_MASK = 255 << FLAG_SYNC_ADAPTER_SHIFT;
|
2011-05-06 20:37:02 +00:00
|
|
|
/** If set, the outgoing message should *not* include the quoted original message. */
|
|
|
|
public static final int FLAG_NOT_INCLUDE_QUOTED_TEXT = 1 << 17;
|
2011-05-09 17:34:57 +00:00
|
|
|
public static final int FLAG_REPLIED_TO = 1 << 18;
|
|
|
|
public static final int FLAG_FORWARDED = 1 << 19;
|
2009-09-07 23:03:02 +00:00
|
|
|
|
2012-06-28 17:40:46 +00:00
|
|
|
// Outgoing, original message
|
|
|
|
public static final int FLAG_TYPE_ORIGINAL = 1 << 20;
|
|
|
|
// Outgoing, reply all message; note, FLAG_TYPE_REPLY should also be set for backward
|
|
|
|
// compatibility
|
|
|
|
public static final int FLAG_TYPE_REPLY_ALL = 1 << 21;
|
|
|
|
|
|
|
|
// Flag used in draftInfo to indicate that the reference message should be appended
|
|
|
|
public static final int DRAFT_INFO_APPEND_REF_MESSAGE = 1 << 24;
|
|
|
|
public static final int DRAFT_INFO_QUOTE_POS_MASK = 0xFFFFFF;
|
|
|
|
|
2011-05-17 17:50:30 +00:00
|
|
|
/** a pseudo ID for "no message". */
|
|
|
|
public static final long NO_MESSAGE = -1L;
|
|
|
|
|
2013-03-13 00:45:11 +00:00
|
|
|
private static final int ATTACHMENT_INDEX_OFFSET = 2;
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
public Message() {
|
|
|
|
mBaseUri = CONTENT_URI;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ContentValues toContentValues() {
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
|
|
|
// Assign values for each row.
|
|
|
|
values.put(MessageColumns.DISPLAY_NAME, mDisplayName);
|
|
|
|
values.put(MessageColumns.TIMESTAMP, mTimeStamp);
|
|
|
|
values.put(MessageColumns.SUBJECT, mSubject);
|
2009-07-30 18:41:31 +00:00
|
|
|
values.put(MessageColumns.FLAG_READ, mFlagRead);
|
2012-12-11 18:37:35 +00:00
|
|
|
values.put(MessageColumns.FLAG_SEEN, mFlagSeen);
|
2009-07-30 18:41:31 +00:00
|
|
|
values.put(MessageColumns.FLAG_LOADED, mFlagLoaded);
|
|
|
|
values.put(MessageColumns.FLAG_FAVORITE, mFlagFavorite);
|
|
|
|
values.put(MessageColumns.FLAG_ATTACHMENT, mFlagAttachment);
|
2009-06-15 21:40:06 +00:00
|
|
|
values.put(MessageColumns.FLAGS, mFlags);
|
2009-09-23 01:31:10 +00:00
|
|
|
values.put(SyncColumns.SERVER_ID, mServerId);
|
|
|
|
values.put(SyncColumns.SERVER_TIMESTAMP, mServerTimeStamp);
|
2012-06-28 17:40:46 +00:00
|
|
|
values.put(MessageColumns.DRAFT_INFO, mDraftInfo);
|
2009-06-15 21:40:06 +00:00
|
|
|
values.put(MessageColumns.MESSAGE_ID, mMessageId);
|
|
|
|
values.put(MessageColumns.MAILBOX_KEY, mMailboxKey);
|
|
|
|
values.put(MessageColumns.ACCOUNT_KEY, mAccountKey);
|
|
|
|
values.put(MessageColumns.FROM_LIST, mFrom);
|
|
|
|
values.put(MessageColumns.TO_LIST, mTo);
|
|
|
|
values.put(MessageColumns.CC_LIST, mCc);
|
|
|
|
values.put(MessageColumns.BCC_LIST, mBcc);
|
|
|
|
values.put(MessageColumns.REPLY_TO_LIST, mReplyTo);
|
2010-02-16 00:01:38 +00:00
|
|
|
values.put(MessageColumns.MEETING_INFO, mMeetingInfo);
|
2010-09-02 02:06:15 +00:00
|
|
|
values.put(MessageColumns.SNIPPET, mSnippet);
|
2011-06-23 17:52:21 +00:00
|
|
|
values.put(MessageColumns.PROTOCOL_SEARCH_INFO, mProtocolSearchInfo);
|
2012-06-28 17:40:46 +00:00
|
|
|
values.put(MessageColumns.THREAD_TOPIC, mThreadTopic);
|
2012-06-29 16:42:05 +00:00
|
|
|
values.put(MessageColumns.SYNC_DATA, mSyncData);
|
Add an additional mailbox key column to message table
b/11294681
The problem is that when we try to open an attachment for a
message in search results, it fails. The reason is that part of
loading the attachment, we need to open the remote folder the
message is in. For search results, the message's mailboxKey is
the special fake "search_results" folder, which doesn't actually
exist on the server.
For this change, I've added a new column called "mainMailboxKey".
For search results, this column will be populated with the real
mailbox the message is in. It will be blank for other messages.
This is a quick and low risk fix for this bug, but it's kind
of awkward. We would prefer to do one or both of the following
some time after MR1.
1. Make the "search_results" folder be a virtual folder, the same
way that unread, starred, and other virtual folders are. For these,
there is actually no mailbox row in the database, just some
queries that check various flags in the messages and behave
like folders in the UI. The messages actually still reside in the
real folders.
2. Remove the requirement to open the folder at all to load the
attachment.
Change-Id: I825ab846f78bf8b041a5d1d579260dc5d7b4c522
2013-10-23 18:18:54 +00:00
|
|
|
values.put(MessageColumns.MAIN_MAILBOX_KEY, mMainMailboxKey);
|
2009-06-15 21:40:06 +00:00
|
|
|
return values;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Message restoreMessageWithId(Context context, long id) {
|
2011-04-28 00:12:06 +00:00
|
|
|
return EmailContent.restoreContentWithId(context, Message.class,
|
|
|
|
Message.CONTENT_URI, Message.CONTENT_PROJECTION, id);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-02 21:23:06 +00:00
|
|
|
public void restore(Cursor cursor) {
|
2009-07-17 23:29:35 +00:00
|
|
|
mBaseUri = CONTENT_URI;
|
2011-02-02 21:23:06 +00:00
|
|
|
mId = cursor.getLong(CONTENT_ID_COLUMN);
|
|
|
|
mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN);
|
|
|
|
mTimeStamp = cursor.getLong(CONTENT_TIMESTAMP_COLUMN);
|
|
|
|
mSubject = cursor.getString(CONTENT_SUBJECT_COLUMN);
|
|
|
|
mFlagRead = cursor.getInt(CONTENT_FLAG_READ_COLUMN) == 1;
|
2012-12-11 18:37:35 +00:00
|
|
|
mFlagSeen = cursor.getInt(CONTENT_FLAG_SEEN_COLUMN) == 1;
|
2011-02-02 21:23:06 +00:00
|
|
|
mFlagLoaded = cursor.getInt(CONTENT_FLAG_LOADED_COLUMN);
|
|
|
|
mFlagFavorite = cursor.getInt(CONTENT_FLAG_FAVORITE_COLUMN) == 1;
|
|
|
|
mFlagAttachment = cursor.getInt(CONTENT_FLAG_ATTACHMENT_COLUMN) == 1;
|
|
|
|
mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
|
|
|
|
mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN);
|
|
|
|
mServerTimeStamp = cursor.getLong(CONTENT_SERVER_TIMESTAMP_COLUMN);
|
2012-06-28 17:40:46 +00:00
|
|
|
mDraftInfo = cursor.getInt(CONTENT_DRAFT_INFO_COLUMN);
|
2011-02-02 21:23:06 +00:00
|
|
|
mMessageId = cursor.getString(CONTENT_MESSAGE_ID_COLUMN);
|
|
|
|
mMailboxKey = cursor.getLong(CONTENT_MAILBOX_KEY_COLUMN);
|
Add an additional mailbox key column to message table
b/11294681
The problem is that when we try to open an attachment for a
message in search results, it fails. The reason is that part of
loading the attachment, we need to open the remote folder the
message is in. For search results, the message's mailboxKey is
the special fake "search_results" folder, which doesn't actually
exist on the server.
For this change, I've added a new column called "mainMailboxKey".
For search results, this column will be populated with the real
mailbox the message is in. It will be blank for other messages.
This is a quick and low risk fix for this bug, but it's kind
of awkward. We would prefer to do one or both of the following
some time after MR1.
1. Make the "search_results" folder be a virtual folder, the same
way that unread, starred, and other virtual folders are. For these,
there is actually no mailbox row in the database, just some
queries that check various flags in the messages and behave
like folders in the UI. The messages actually still reside in the
real folders.
2. Remove the requirement to open the folder at all to load the
attachment.
Change-Id: I825ab846f78bf8b041a5d1d579260dc5d7b4c522
2013-10-23 18:18:54 +00:00
|
|
|
mMainMailboxKey = cursor.getLong(CONTENT_MAIN_MAILBOX_KEY_COLUMN);
|
2011-02-02 21:23:06 +00:00
|
|
|
mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
|
|
|
|
mFrom = cursor.getString(CONTENT_FROM_LIST_COLUMN);
|
|
|
|
mTo = cursor.getString(CONTENT_TO_LIST_COLUMN);
|
|
|
|
mCc = cursor.getString(CONTENT_CC_LIST_COLUMN);
|
|
|
|
mBcc = cursor.getString(CONTENT_BCC_LIST_COLUMN);
|
|
|
|
mReplyTo = cursor.getString(CONTENT_REPLY_TO_COLUMN);
|
|
|
|
mMeetingInfo = cursor.getString(CONTENT_MEETING_INFO_COLUMN);
|
|
|
|
mSnippet = cursor.getString(CONTENT_SNIPPET_COLUMN);
|
2011-06-23 17:52:21 +00:00
|
|
|
mProtocolSearchInfo = cursor.getString(CONTENT_PROTOCOL_SEARCH_INFO_COLUMN);
|
2012-06-28 17:40:46 +00:00
|
|
|
mThreadTopic = cursor.getString(CONTENT_THREAD_TOPIC_COLUMN);
|
2012-06-29 16:42:05 +00:00
|
|
|
mSyncData = cursor.getString(CONTENT_SYNC_DATA_COLUMN);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2009-07-30 18:41:31 +00:00
|
|
|
/*
|
2009-06-15 21:40:06 +00:00
|
|
|
* Override this so that we can store the Body first and link it to the Message
|
|
|
|
* Also, attachments when we get there...
|
|
|
|
* (non-Javadoc)
|
|
|
|
* @see com.android.email.provider.EmailContent#save(android.content.Context)
|
|
|
|
*/
|
2009-07-30 18:41:31 +00:00
|
|
|
@Override
|
2009-06-15 21:40:06 +00:00
|
|
|
public Uri save(Context context) {
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
boolean doSave = !isSaved();
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
// This logic is in place so I can (a) short circuit the expensive stuff when
|
|
|
|
// possible, and (b) override (and throw) if anyone tries to call save() or update()
|
|
|
|
// directly for Message, which are unsupported.
|
2014-04-18 21:04:23 +00:00
|
|
|
if (mText == null && mHtml == null &&
|
2009-07-16 23:03:40 +00:00
|
|
|
(mAttachments == null || mAttachments.isEmpty())) {
|
2009-06-15 21:40:06 +00:00
|
|
|
if (doSave) {
|
|
|
|
return super.save(context);
|
|
|
|
} else {
|
2013-11-11 20:56:59 +00:00
|
|
|
// FLAG: Should we be doing this? In the base class, if someone calls "save" on
|
|
|
|
// an EmailContent that is already saved, it throws an exception.
|
2009-06-15 21:40:06 +00:00
|
|
|
// Call update, rather than super.update in case we ever override it
|
|
|
|
if (update(context, toContentValues()) == 1) {
|
|
|
|
return getUri();
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2013-03-13 00:45:11 +00:00
|
|
|
final ArrayList<ContentProviderOperation> ops =
|
|
|
|
new ArrayList<ContentProviderOperation>();
|
2009-06-16 18:26:40 +00:00
|
|
|
addSaveOps(ops);
|
2009-06-15 21:40:06 +00:00
|
|
|
try {
|
2013-03-13 00:45:11 +00:00
|
|
|
final ContentProviderResult[] results =
|
2011-02-11 23:05:17 +00:00
|
|
|
context.getContentResolver().applyBatch(AUTHORITY, ops);
|
2009-06-15 21:40:06 +00:00
|
|
|
// If saving, set the mId's of the various saved objects
|
|
|
|
if (doSave) {
|
|
|
|
Uri u = results[0].uri;
|
|
|
|
mId = Long.parseLong(u.getPathSegments().get(1));
|
2009-07-16 23:03:40 +00:00
|
|
|
if (mAttachments != null) {
|
2013-03-13 00:45:11 +00:00
|
|
|
// Skip over the first two items in the result array
|
|
|
|
for (int i = 0; i < mAttachments.size(); i++) {
|
|
|
|
final Attachment a = mAttachments.get(i);
|
|
|
|
|
|
|
|
final int resultIndex = i + ATTACHMENT_INDEX_OFFSET;
|
2009-07-17 23:29:35 +00:00
|
|
|
// Save the id of the attachment record
|
2013-03-13 00:45:11 +00:00
|
|
|
if (resultIndex < results.length) {
|
|
|
|
u = results[resultIndex].uri;
|
|
|
|
} else {
|
|
|
|
// We didn't find the expected attachment, log this error
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.e(LOG_TAG, "Invalid index into ContentProviderResults: " +
|
2013-03-13 00:45:11 +00:00
|
|
|
resultIndex);
|
|
|
|
u = null;
|
|
|
|
}
|
2009-07-17 23:29:35 +00:00
|
|
|
if (u != null) {
|
|
|
|
a.mId = Long.parseLong(u.getPathSegments().get(1));
|
|
|
|
}
|
2009-07-16 23:03:40 +00:00
|
|
|
a.mMessageKey = mId;
|
|
|
|
}
|
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
return u;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} catch (RemoteException e) {
|
|
|
|
// There is nothing to be done here; fail by returning null
|
|
|
|
} catch (OperationApplicationException e) {
|
|
|
|
// There is nothing to be done here; fail by returning null
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-06-28 17:40:46 +00:00
|
|
|
/**
|
|
|
|
* Save or update a message
|
|
|
|
* @param ops an array of CPOs that we'll add to
|
|
|
|
*/
|
2009-06-15 21:40:06 +00:00
|
|
|
public void addSaveOps(ArrayList<ContentProviderOperation> ops) {
|
2012-06-28 17:40:46 +00:00
|
|
|
boolean isNew = !isSaved();
|
|
|
|
ContentProviderOperation.Builder b;
|
|
|
|
// First, save/update the message
|
|
|
|
if (isNew) {
|
|
|
|
b = ContentProviderOperation.newInsert(mBaseUri);
|
|
|
|
} else {
|
|
|
|
b = ContentProviderOperation.newUpdate(mBaseUri)
|
2014-04-11 21:42:28 +00:00
|
|
|
.withSelection(MessageColumns._ID + "=?",
|
|
|
|
new String[] {Long.toString(mId)});
|
2012-06-28 17:40:46 +00:00
|
|
|
}
|
2010-09-02 02:06:15 +00:00
|
|
|
// Generate the snippet here, before we create the CPO for Message
|
|
|
|
if (mText != null) {
|
2011-03-27 02:19:35 +00:00
|
|
|
mSnippet = TextUtilities.makeSnippetFromPlainText(mText);
|
2010-09-02 02:06:15 +00:00
|
|
|
} else if (mHtml != null) {
|
2011-03-27 02:19:35 +00:00
|
|
|
mSnippet = TextUtilities.makeSnippetFromHtmlText(mHtml);
|
2010-09-02 02:06:15 +00:00
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
ops.add(b.withValues(toContentValues()).build());
|
2009-07-17 23:29:35 +00:00
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
// Create and save the body
|
|
|
|
ContentValues cv = new ContentValues();
|
|
|
|
if (mText != null) {
|
2014-04-11 21:42:28 +00:00
|
|
|
cv.put(BodyColumns.TEXT_CONTENT, mText);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
if (mHtml != null) {
|
2014-04-11 21:42:28 +00:00
|
|
|
cv.put(BodyColumns.HTML_CONTENT, mHtml);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
2009-09-07 23:03:02 +00:00
|
|
|
if (mSourceKey != 0) {
|
2014-04-11 21:42:28 +00:00
|
|
|
cv.put(BodyColumns.SOURCE_MESSAGE_KEY, mSourceKey);
|
2009-09-07 23:03:02 +00:00
|
|
|
}
|
2012-06-28 17:40:46 +00:00
|
|
|
if (mQuotedTextStartPos != 0) {
|
2014-04-11 21:42:28 +00:00
|
|
|
cv.put(BodyColumns.QUOTED_TEXT_START_POS, mQuotedTextStartPos);
|
2009-09-23 01:38:28 +00:00
|
|
|
}
|
2012-06-28 17:40:46 +00:00
|
|
|
// We'll need this if we're new
|
2009-07-15 22:08:53 +00:00
|
|
|
int messageBackValue = ops.size() - 1;
|
2012-06-29 16:42:05 +00:00
|
|
|
// Only create a body if we've got some data
|
|
|
|
if (!cv.keySet().isEmpty()) {
|
|
|
|
b = ContentProviderOperation.newInsert(Body.CONTENT_URI);
|
|
|
|
// Put our message id in the Body
|
|
|
|
if (!isNew) {
|
2014-04-11 21:42:28 +00:00
|
|
|
cv.put(BodyColumns.MESSAGE_KEY, mId);
|
2012-06-29 16:42:05 +00:00
|
|
|
}
|
|
|
|
b.withValues(cv);
|
|
|
|
// If we're new, create a back value entry
|
|
|
|
if (isNew) {
|
|
|
|
ContentValues backValues = new ContentValues();
|
2014-04-11 21:42:28 +00:00
|
|
|
backValues.put(BodyColumns.MESSAGE_KEY, messageBackValue);
|
2012-06-29 16:42:05 +00:00
|
|
|
b.withValueBackReferences(backValues);
|
|
|
|
}
|
|
|
|
// And add the Body operation
|
|
|
|
ops.add(b.build());
|
2012-06-28 17:40:46 +00:00
|
|
|
}
|
2009-07-15 22:08:53 +00:00
|
|
|
|
|
|
|
// Create the attaachments, if any
|
|
|
|
if (mAttachments != null) {
|
|
|
|
for (Attachment att: mAttachments) {
|
2012-06-28 17:40:46 +00:00
|
|
|
if (!isNew) {
|
|
|
|
att.mMessageKey = mId;
|
|
|
|
}
|
|
|
|
b = ContentProviderOperation.newInsert(Attachment.CONTENT_URI)
|
|
|
|
.withValues(att.toContentValues());
|
|
|
|
if (isNew) {
|
2014-04-11 21:42:28 +00:00
|
|
|
b.withValueBackReference(AttachmentColumns.MESSAGE_KEY, messageBackValue);
|
2012-06-28 17:40:46 +00:00
|
|
|
}
|
|
|
|
ops.add(b.build());
|
2009-07-15 22:08:53 +00:00
|
|
|
}
|
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
2010-08-03 01:16:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return number of favorite (starred) messages throughout all accounts.
|
|
|
|
*/
|
|
|
|
public static int getFavoriteMessageCount(Context context) {
|
2011-01-13 00:38:25 +00:00
|
|
|
return count(context, Message.CONTENT_URI, ALL_FAVORITE_SELECTION, null);
|
2010-08-03 01:16:13 +00:00
|
|
|
}
|
2010-08-24 04:39:35 +00:00
|
|
|
|
2011-01-12 00:43:06 +00:00
|
|
|
/**
|
|
|
|
* @return number of favorite (starred) messages for an account
|
|
|
|
*/
|
|
|
|
public static int getFavoriteMessageCount(Context context, long accountId) {
|
2011-06-01 19:29:10 +00:00
|
|
|
return count(context, Message.CONTENT_URI, PER_ACCOUNT_FAVORITE_SELECTION,
|
2011-01-12 00:43:06 +00:00
|
|
|
new String[]{Long.toString(accountId)});
|
|
|
|
}
|
|
|
|
|
2010-08-24 04:39:35 +00:00
|
|
|
public static long getKeyColumnLong(Context context, long messageId, String column) {
|
|
|
|
String[] columns =
|
|
|
|
Utility.getRowColumns(context, Message.CONTENT_URI, messageId, column);
|
|
|
|
if (columns != null && columns[0] != null) {
|
|
|
|
return Long.parseLong(columns[0]);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2011-05-13 21:09:22 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the where clause for a message list selection.
|
|
|
|
*
|
|
|
|
* Accesses the detabase to determine the mailbox type. DO NOT CALL FROM UI THREAD.
|
|
|
|
*/
|
2011-10-12 23:24:24 +00:00
|
|
|
public static String buildMessageListSelection(
|
|
|
|
Context context, long accountId, long mailboxId) {
|
2011-05-13 21:09:22 +00:00
|
|
|
|
|
|
|
if (mailboxId == Mailbox.QUERY_ALL_INBOXES) {
|
|
|
|
return Message.ALL_INBOX_SELECTION;
|
|
|
|
}
|
|
|
|
if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) {
|
|
|
|
return Message.ALL_DRAFT_SELECTION;
|
|
|
|
}
|
|
|
|
if (mailboxId == Mailbox.QUERY_ALL_OUTBOX) {
|
|
|
|
return Message.ALL_OUTBOX_SELECTION;
|
|
|
|
}
|
|
|
|
if (mailboxId == Mailbox.QUERY_ALL_UNREAD) {
|
|
|
|
return Message.ALL_UNREAD_SELECTION;
|
|
|
|
}
|
2011-10-12 23:24:24 +00:00
|
|
|
// TODO: we only support per-account starred mailbox right now, but presumably, we
|
|
|
|
// can surface the same thing for unread.
|
2011-05-13 21:09:22 +00:00
|
|
|
if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) {
|
2011-10-12 23:24:24 +00:00
|
|
|
if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
|
|
|
|
return Message.ALL_FAVORITE_SELECTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
final StringBuilder selection = new StringBuilder();
|
|
|
|
selection.append(MessageColumns.ACCOUNT_KEY).append('=').append(accountId)
|
|
|
|
.append(" AND ")
|
|
|
|
.append(Message.ALL_FAVORITE_SELECTION);
|
|
|
|
return selection.toString();
|
2011-05-13 21:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now it's a regular mailbox.
|
|
|
|
final StringBuilder selection = new StringBuilder();
|
|
|
|
|
|
|
|
selection.append(MessageColumns.MAILBOX_KEY).append('=').append(mailboxId);
|
|
|
|
|
|
|
|
if (Mailbox.getMailboxType(context, mailboxId) != Mailbox.TYPE_OUTBOX) {
|
|
|
|
selection.append(" AND ").append(Message.FLAG_LOADED_SELECTION);
|
|
|
|
}
|
|
|
|
return selection.toString();
|
|
|
|
}
|
2013-06-04 22:08:18 +00:00
|
|
|
|
|
|
|
public void setFlags(boolean quotedReply, boolean quotedForward) {
|
|
|
|
// Set message flags as well
|
|
|
|
if (quotedReply || quotedForward) {
|
2014-05-08 20:07:54 +00:00
|
|
|
mFlags &= ~Message.FLAG_TYPE_MASK;
|
2013-06-04 22:08:18 +00:00
|
|
|
mFlags |= quotedReply
|
2014-05-08 20:07:54 +00:00
|
|
|
? Message.FLAG_TYPE_REPLY
|
|
|
|
: Message.FLAG_TYPE_FORWARD;
|
2013-06-04 22:08:18 +00:00
|
|
|
}
|
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface AttachmentColumns extends BaseColumns {
|
2009-07-30 18:41:31 +00:00
|
|
|
// The display name of the attachment
|
2009-06-15 21:40:06 +00:00
|
|
|
public static final String FILENAME = "fileName";
|
|
|
|
// The mime type of the attachment
|
|
|
|
public static final String MIME_TYPE = "mimeType";
|
|
|
|
// The size of the attachment in bytes
|
|
|
|
public static final String SIZE = "size";
|
|
|
|
// The (internal) contentId of the attachment (inline attachments will have these)
|
|
|
|
public static final String CONTENT_ID = "contentId";
|
|
|
|
// The location of the loaded attachment (probably a file)
|
2010-09-10 22:19:57 +00:00
|
|
|
@SuppressWarnings("hiding")
|
2009-06-15 21:40:06 +00:00
|
|
|
public static final String CONTENT_URI = "contentUri";
|
2013-02-23 03:42:40 +00:00
|
|
|
// The cached location of the attachment
|
|
|
|
public static final String CACHED_FILE = "cachedFile";
|
2009-06-15 21:40:06 +00:00
|
|
|
// A foreign key into the Message table (the message owning this attachment)
|
|
|
|
public static final String MESSAGE_KEY = "messageKey";
|
|
|
|
// The location of the attachment on the server side
|
|
|
|
// For IMAP, this is a part number (e.g. 2.1); for EAS, it's the internal file name
|
|
|
|
public static final String LOCATION = "location";
|
|
|
|
// The transfer encoding of the attachment
|
|
|
|
public static final String ENCODING = "encoding";
|
2010-03-12 21:30:26 +00:00
|
|
|
// Not currently used
|
2010-02-22 20:57:33 +00:00
|
|
|
public static final String CONTENT = "content";
|
|
|
|
// Flags
|
|
|
|
public static final String FLAGS = "flags";
|
2010-03-12 21:30:26 +00:00
|
|
|
// Content that is actually contained in the Attachment row
|
|
|
|
public static final String CONTENT_BYTES = "content_bytes";
|
2010-12-09 01:11:04 +00:00
|
|
|
// A foreign key into the Account table (for the message owning this attachment)
|
|
|
|
public static final String ACCOUNT_KEY = "accountKey";
|
2012-06-28 17:40:46 +00:00
|
|
|
// The UIProvider state of the attachment
|
|
|
|
public static final String UI_STATE = "uiState";
|
|
|
|
// The UIProvider destination of the attachment
|
|
|
|
public static final String UI_DESTINATION = "uiDestination";
|
|
|
|
// The UIProvider downloaded size of the attachment
|
|
|
|
public static final String UI_DOWNLOADED_SIZE = "uiDownloadedSize";
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final class Attachment extends EmailContent implements Parcelable {
|
2009-06-16 19:03:45 +00:00
|
|
|
public static final String TABLE_NAME = "Attachment";
|
2012-09-13 20:55:19 +00:00
|
|
|
public static final String ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX =
|
|
|
|
"content://com.android.email.attachmentprovider";
|
2012-08-23 05:25:42 +00:00
|
|
|
|
2013-03-21 23:54:49 +00:00
|
|
|
public static final String CACHED_FILE_QUERY_PARAM = "filePath";
|
|
|
|
|
2012-08-23 05:25:42 +00:00
|
|
|
public static Uri CONTENT_URI;
|
2009-07-16 23:03:40 +00:00
|
|
|
// This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id)
|
2012-08-23 05:25:42 +00:00
|
|
|
public static Uri MESSAGE_ID_URI;
|
2012-09-08 17:36:32 +00:00
|
|
|
public static String ATTACHMENT_PROVIDER_URI_PREFIX;
|
2014-04-08 21:18:56 +00:00
|
|
|
public static String ATTACHMENT_PROVIDER_AUTHORITY;
|
2012-09-13 20:55:19 +00:00
|
|
|
public static boolean sUsingLegacyPrefix;
|
2012-08-23 05:25:42 +00:00
|
|
|
|
|
|
|
public static void initAttachment() {
|
|
|
|
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment");
|
|
|
|
MESSAGE_ID_URI = Uri.parse(
|
|
|
|
EmailContent.CONTENT_URI + "/attachment/message");
|
2014-04-08 21:18:56 +00:00
|
|
|
ATTACHMENT_PROVIDER_AUTHORITY = EmailContent.EMAIL_PACKAGE_NAME +
|
2012-09-08 17:36:32 +00:00
|
|
|
".attachmentprovider";
|
2014-04-08 21:18:56 +00:00
|
|
|
ATTACHMENT_PROVIDER_URI_PREFIX = "content://" + ATTACHMENT_PROVIDER_AUTHORITY;
|
2012-09-13 20:55:19 +00:00
|
|
|
sUsingLegacyPrefix =
|
|
|
|
ATTACHMENT_PROVIDER_URI_PREFIX.equals(ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX);
|
2012-08-23 05:25:42 +00:00
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
|
|
|
|
public String mFileName;
|
|
|
|
public String mMimeType;
|
|
|
|
public long mSize;
|
|
|
|
public String mContentId;
|
2012-09-08 17:36:32 +00:00
|
|
|
private String mContentUri;
|
2013-03-21 23:54:49 +00:00
|
|
|
private String mCachedFileUri;
|
2009-06-15 21:40:06 +00:00
|
|
|
public long mMessageKey;
|
|
|
|
public String mLocation;
|
|
|
|
public String mEncoding;
|
2010-03-12 21:30:26 +00:00
|
|
|
public String mContent; // Not currently used
|
2010-02-22 20:57:33 +00:00
|
|
|
public int mFlags;
|
2010-03-12 21:30:26 +00:00
|
|
|
public byte[] mContentBytes;
|
2010-12-09 01:11:04 +00:00
|
|
|
public long mAccountKey;
|
2012-06-28 17:40:46 +00:00
|
|
|
public int mUiState;
|
|
|
|
public int mUiDestination;
|
|
|
|
public int mUiDownloadedSize;
|
2009-06-15 21:40:06 +00:00
|
|
|
|
|
|
|
public static final int CONTENT_ID_COLUMN = 0;
|
|
|
|
public static final int CONTENT_FILENAME_COLUMN = 1;
|
|
|
|
public static final int CONTENT_MIME_TYPE_COLUMN = 2;
|
|
|
|
public static final int CONTENT_SIZE_COLUMN = 3;
|
|
|
|
public static final int CONTENT_CONTENT_ID_COLUMN = 4;
|
|
|
|
public static final int CONTENT_CONTENT_URI_COLUMN = 5;
|
2013-02-23 03:42:40 +00:00
|
|
|
public static final int CONTENT_CACHED_FILE_COLUMN = 6;
|
|
|
|
public static final int CONTENT_MESSAGE_ID_COLUMN = 7;
|
|
|
|
public static final int CONTENT_LOCATION_COLUMN = 8;
|
|
|
|
public static final int CONTENT_ENCODING_COLUMN = 9;
|
|
|
|
public static final int CONTENT_CONTENT_COLUMN = 10; // Not currently used
|
|
|
|
public static final int CONTENT_FLAGS_COLUMN = 11;
|
|
|
|
public static final int CONTENT_CONTENT_BYTES_COLUMN = 12;
|
|
|
|
public static final int CONTENT_ACCOUNT_KEY_COLUMN = 13;
|
|
|
|
public static final int CONTENT_UI_STATE_COLUMN = 14;
|
|
|
|
public static final int CONTENT_UI_DESTINATION_COLUMN = 15;
|
|
|
|
public static final int CONTENT_UI_DOWNLOADED_SIZE_COLUMN = 16;
|
2014-04-11 21:42:28 +00:00
|
|
|
public static final String[] CONTENT_PROJECTION = {
|
|
|
|
AttachmentColumns._ID, AttachmentColumns.FILENAME, AttachmentColumns.MIME_TYPE,
|
2009-06-15 21:40:06 +00:00
|
|
|
AttachmentColumns.SIZE, AttachmentColumns.CONTENT_ID, AttachmentColumns.CONTENT_URI,
|
2013-02-23 03:42:40 +00:00
|
|
|
AttachmentColumns.CACHED_FILE, AttachmentColumns.MESSAGE_KEY,
|
|
|
|
AttachmentColumns.LOCATION, AttachmentColumns.ENCODING, AttachmentColumns.CONTENT,
|
|
|
|
AttachmentColumns.FLAGS, AttachmentColumns.CONTENT_BYTES, AttachmentColumns.ACCOUNT_KEY,
|
|
|
|
AttachmentColumns.UI_STATE, AttachmentColumns.UI_DESTINATION,
|
|
|
|
AttachmentColumns.UI_DOWNLOADED_SIZE
|
2009-06-15 21:40:06 +00:00
|
|
|
};
|
|
|
|
|
2011-01-21 19:58:39 +00:00
|
|
|
// All attachments with an empty URI, regardless of mailbox
|
2011-05-12 00:15:59 +00:00
|
|
|
public static final String PRECACHE_SELECTION =
|
2014-04-11 21:42:28 +00:00
|
|
|
AttachmentColumns.CONTENT_URI + " isnull AND " + AttachmentColumns.FLAGS + "=0";
|
2011-01-21 19:58:39 +00:00
|
|
|
// Attachments with an empty URI that are in an inbox
|
2011-05-12 00:15:59 +00:00
|
|
|
public static final String PRECACHE_INBOX_SELECTION =
|
|
|
|
PRECACHE_SELECTION + " AND " + AttachmentColumns.MESSAGE_KEY + " IN ("
|
2014-04-11 21:42:28 +00:00
|
|
|
+ "SELECT " + MessageColumns._ID + " FROM " + Message.TABLE_NAME
|
2011-05-13 21:09:22 +00:00
|
|
|
+ " WHERE " + Message.ALL_INBOX_SELECTION
|
2011-01-21 19:58:39 +00:00
|
|
|
+ ")";
|
|
|
|
|
2010-02-22 20:57:33 +00:00
|
|
|
// Bits used in mFlags
|
2011-05-12 00:15:59 +00:00
|
|
|
// WARNING: AttachmentDownloadService relies on the fact that ALL of the flags below
|
|
|
|
// disqualify attachments for precaching. If you add a flag that does NOT disqualify an
|
|
|
|
// attachment for precaching, you MUST change the PRECACHE_SELECTION definition above
|
|
|
|
|
2010-03-16 20:38:47 +00:00
|
|
|
// Instruct Rfc822Output to 1) not use Content-Disposition and 2) use multipart/alternative
|
|
|
|
// with this attachment. This is only valid if there is one and only one attachment and
|
|
|
|
// that attachment has this flag set
|
|
|
|
public static final int FLAG_ICS_ALTERNATIVE_PART = 1<<0;
|
2010-08-10 00:48:53 +00:00
|
|
|
// Indicate that this attachment has been requested for downloading by the user; this is
|
|
|
|
// the highest priority for attachment downloading
|
|
|
|
public static final int FLAG_DOWNLOAD_USER_REQUEST = 1<<1;
|
|
|
|
// Indicate that this attachment needs to be downloaded as part of an outgoing forwarded
|
|
|
|
// message
|
|
|
|
public static final int FLAG_DOWNLOAD_FORWARD = 1<<2;
|
|
|
|
// Indicates that the attachment download failed in a non-recoverable manner
|
|
|
|
public static final int FLAG_DOWNLOAD_FAILED = 1<<3;
|
2011-01-26 23:02:13 +00:00
|
|
|
// Allow "room" for some additional download-related flags here
|
|
|
|
// Indicates that the attachment will be smart-forwarded
|
|
|
|
public static final int FLAG_SMART_FORWARD = 1<<8;
|
2011-05-11 01:10:02 +00:00
|
|
|
// Indicates that the attachment cannot be forwarded due to a policy restriction
|
|
|
|
public static final int FLAG_POLICY_DISALLOWS_DOWNLOAD = 1<<9;
|
2013-09-21 00:34:09 +00:00
|
|
|
// Indicates that this is a dummy placeholder attachment.
|
|
|
|
public static final int FLAG_DUMMY_ATTACHMENT = 1<<10;
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
/**
|
|
|
|
* no public constructor since this is a utility class
|
|
|
|
*/
|
|
|
|
public Attachment() {
|
|
|
|
mBaseUri = CONTENT_URI;
|
|
|
|
}
|
2009-07-30 18:41:31 +00:00
|
|
|
|
2013-03-21 23:54:49 +00:00
|
|
|
public void setCachedFileUri(String cachedFile) {
|
|
|
|
mCachedFileUri = cachedFile;
|
2013-02-23 03:42:40 +00:00
|
|
|
}
|
|
|
|
|
2013-03-21 23:54:49 +00:00
|
|
|
public String getCachedFileUri() {
|
|
|
|
return mCachedFileUri;
|
2013-02-23 03:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-09-08 17:36:32 +00:00
|
|
|
public void setContentUri(String contentUri) {
|
|
|
|
mContentUri = contentUri;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getContentUri() {
|
2012-09-13 20:55:19 +00:00
|
|
|
if (mContentUri == null) return null; //
|
|
|
|
// If we're not using the legacy prefix and the uri IS, we need to modify it
|
|
|
|
if (!Attachment.sUsingLegacyPrefix &&
|
|
|
|
mContentUri.startsWith(Attachment.ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX)) {
|
2012-09-08 17:36:32 +00:00
|
|
|
// In an upgrade scenario, we may still have legacy attachment Uri's
|
|
|
|
// Skip past content://
|
|
|
|
int prefix = mContentUri.indexOf('/', 10);
|
|
|
|
if (prefix > 0) {
|
|
|
|
// Create a proper uri string using the actual provider
|
|
|
|
return ATTACHMENT_PROVIDER_URI_PREFIX + "/" + mContentUri.substring(prefix);
|
|
|
|
} else {
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.e("Attachment", "Improper contentUri format: " + mContentUri);
|
2012-09-08 17:36:32 +00:00
|
|
|
// Belt & suspenders; can't really happen
|
|
|
|
return mContentUri;
|
|
|
|
}
|
2012-09-13 20:55:19 +00:00
|
|
|
} else {
|
|
|
|
return mContentUri;
|
2012-09-08 17:36:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
/**
|
|
|
|
* Restore an Attachment from the database, given its unique id
|
|
|
|
* @param context
|
|
|
|
* @param id
|
|
|
|
* @return the instantiated Attachment
|
|
|
|
*/
|
2011-05-06 18:20:37 +00:00
|
|
|
public static Attachment restoreAttachmentWithId(Context context, long id) {
|
2011-04-28 00:12:06 +00:00
|
|
|
return EmailContent.restoreContentWithId(context, Attachment.class,
|
|
|
|
Attachment.CONTENT_URI, Attachment.CONTENT_PROJECTION, id);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
2009-08-18 16:55:59 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Restore all the Attachments of a message given its messageId
|
|
|
|
*/
|
|
|
|
public static Attachment[] restoreAttachmentsWithMessageId(Context context,
|
|
|
|
long messageId) {
|
|
|
|
Uri uri = ContentUris.withAppendedId(MESSAGE_ID_URI, messageId);
|
|
|
|
Cursor c = context.getContentResolver().query(uri, CONTENT_PROJECTION,
|
|
|
|
null, null, null);
|
|
|
|
try {
|
|
|
|
int count = c.getCount();
|
|
|
|
Attachment[] attachments = new Attachment[count];
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
c.moveToNext();
|
2011-02-02 21:23:06 +00:00
|
|
|
Attachment attach = new Attachment();
|
|
|
|
attach.restore(c);
|
|
|
|
attachments[i] = attach;
|
2009-08-18 16:55:59 +00:00
|
|
|
}
|
|
|
|
return attachments;
|
|
|
|
} finally {
|
|
|
|
c.close();
|
|
|
|
}
|
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
|
2009-07-15 22:08:53 +00:00
|
|
|
/**
|
|
|
|
* Creates a unique file in the external store by appending a hyphen
|
|
|
|
* and a number to the given filename.
|
|
|
|
* @param filename
|
|
|
|
* @return a new File object, or null if one could not be created
|
|
|
|
*/
|
|
|
|
public static File createUniqueFile(String filename) {
|
|
|
|
// TODO Handle internal storage, as required
|
|
|
|
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
|
|
|
File directory = Environment.getExternalStorageDirectory();
|
|
|
|
File file = new File(directory, filename);
|
|
|
|
if (!file.exists()) {
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
// Get the extension of the file, if any.
|
|
|
|
int index = filename.lastIndexOf('.');
|
|
|
|
String name = filename;
|
|
|
|
String extension = "";
|
|
|
|
if (index != -1) {
|
|
|
|
name = filename.substring(0, index);
|
|
|
|
extension = filename.substring(index);
|
|
|
|
}
|
|
|
|
for (int i = 2; i < Integer.MAX_VALUE; i++) {
|
|
|
|
file = new File(directory, name + '-' + i + extension);
|
|
|
|
if (!file.exists()) {
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2009-06-15 21:40:06 +00:00
|
|
|
@Override
|
2011-02-02 21:23:06 +00:00
|
|
|
public void restore(Cursor cursor) {
|
2009-07-17 23:29:35 +00:00
|
|
|
mBaseUri = CONTENT_URI;
|
|
|
|
mId = cursor.getLong(CONTENT_ID_COLUMN);
|
2009-06-15 21:40:06 +00:00
|
|
|
mFileName= cursor.getString(CONTENT_FILENAME_COLUMN);
|
|
|
|
mMimeType = cursor.getString(CONTENT_MIME_TYPE_COLUMN);
|
|
|
|
mSize = cursor.getLong(CONTENT_SIZE_COLUMN);
|
|
|
|
mContentId = cursor.getString(CONTENT_CONTENT_ID_COLUMN);
|
|
|
|
mContentUri = cursor.getString(CONTENT_CONTENT_URI_COLUMN);
|
2013-03-21 23:54:49 +00:00
|
|
|
mCachedFileUri = cursor.getString(CONTENT_CACHED_FILE_COLUMN);
|
2009-06-15 21:40:06 +00:00
|
|
|
mMessageKey = cursor.getLong(CONTENT_MESSAGE_ID_COLUMN);
|
|
|
|
mLocation = cursor.getString(CONTENT_LOCATION_COLUMN);
|
|
|
|
mEncoding = cursor.getString(CONTENT_ENCODING_COLUMN);
|
2010-02-22 20:57:33 +00:00
|
|
|
mContent = cursor.getString(CONTENT_CONTENT_COLUMN);
|
|
|
|
mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
|
2010-03-12 21:30:26 +00:00
|
|
|
mContentBytes = cursor.getBlob(CONTENT_CONTENT_BYTES_COLUMN);
|
2010-12-09 01:11:04 +00:00
|
|
|
mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
|
2012-06-28 17:40:46 +00:00
|
|
|
mUiState = cursor.getInt(CONTENT_UI_STATE_COLUMN);
|
|
|
|
mUiDestination = cursor.getInt(CONTENT_UI_DESTINATION_COLUMN);
|
|
|
|
mUiDownloadedSize = cursor.getInt(CONTENT_UI_DOWNLOADED_SIZE_COLUMN);
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ContentValues toContentValues() {
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
values.put(AttachmentColumns.FILENAME, mFileName);
|
2009-07-30 18:41:31 +00:00
|
|
|
values.put(AttachmentColumns.MIME_TYPE, mMimeType);
|
|
|
|
values.put(AttachmentColumns.SIZE, mSize);
|
|
|
|
values.put(AttachmentColumns.CONTENT_ID, mContentId);
|
|
|
|
values.put(AttachmentColumns.CONTENT_URI, mContentUri);
|
2013-03-21 23:54:49 +00:00
|
|
|
values.put(AttachmentColumns.CACHED_FILE, mCachedFileUri);
|
2009-07-30 18:41:31 +00:00
|
|
|
values.put(AttachmentColumns.MESSAGE_KEY, mMessageKey);
|
|
|
|
values.put(AttachmentColumns.LOCATION, mLocation);
|
|
|
|
values.put(AttachmentColumns.ENCODING, mEncoding);
|
2010-02-22 20:57:33 +00:00
|
|
|
values.put(AttachmentColumns.CONTENT, mContent);
|
|
|
|
values.put(AttachmentColumns.FLAGS, mFlags);
|
2010-03-12 21:30:26 +00:00
|
|
|
values.put(AttachmentColumns.CONTENT_BYTES, mContentBytes);
|
2010-12-09 01:11:04 +00:00
|
|
|
values.put(AttachmentColumns.ACCOUNT_KEY, mAccountKey);
|
2012-06-28 17:40:46 +00:00
|
|
|
values.put(AttachmentColumns.UI_STATE, mUiState);
|
|
|
|
values.put(AttachmentColumns.UI_DESTINATION, mUiDestination);
|
|
|
|
values.put(AttachmentColumns.UI_DOWNLOADED_SIZE, mUiDownloadedSize);
|
2009-06-15 21:40:06 +00:00
|
|
|
return values;
|
|
|
|
}
|
2009-06-16 19:03:45 +00:00
|
|
|
|
2011-05-06 18:20:37 +00:00
|
|
|
@Override
|
2009-06-16 19:03:45 +00:00
|
|
|
public int describeContents() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-06 18:20:37 +00:00
|
|
|
@Override
|
2009-06-16 19:03:45 +00:00
|
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
|
|
// mBaseUri is not parceled
|
|
|
|
dest.writeLong(mId);
|
|
|
|
dest.writeString(mFileName);
|
|
|
|
dest.writeString(mMimeType);
|
|
|
|
dest.writeLong(mSize);
|
|
|
|
dest.writeString(mContentId);
|
|
|
|
dest.writeString(mContentUri);
|
2013-03-21 23:54:49 +00:00
|
|
|
dest.writeString(mCachedFileUri);
|
2009-06-16 19:03:45 +00:00
|
|
|
dest.writeLong(mMessageKey);
|
|
|
|
dest.writeString(mLocation);
|
|
|
|
dest.writeString(mEncoding);
|
2010-02-22 20:57:33 +00:00
|
|
|
dest.writeString(mContent);
|
|
|
|
dest.writeInt(mFlags);
|
2010-12-09 01:11:04 +00:00
|
|
|
dest.writeLong(mAccountKey);
|
2010-03-12 21:30:26 +00:00
|
|
|
if (mContentBytes == null) {
|
|
|
|
dest.writeInt(-1);
|
|
|
|
} else {
|
|
|
|
dest.writeInt(mContentBytes.length);
|
|
|
|
dest.writeByteArray(mContentBytes);
|
|
|
|
}
|
2012-06-28 17:40:46 +00:00
|
|
|
dest.writeInt(mUiState);
|
|
|
|
dest.writeInt(mUiDestination);
|
|
|
|
dest.writeInt(mUiDownloadedSize);
|
2009-06-16 19:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public Attachment(Parcel in) {
|
2014-05-08 20:07:54 +00:00
|
|
|
mBaseUri = Attachment.CONTENT_URI;
|
2009-06-16 19:03:45 +00:00
|
|
|
mId = in.readLong();
|
|
|
|
mFileName = in.readString();
|
|
|
|
mMimeType = in.readString();
|
|
|
|
mSize = in.readLong();
|
|
|
|
mContentId = in.readString();
|
|
|
|
mContentUri = in.readString();
|
2013-03-21 23:54:49 +00:00
|
|
|
mCachedFileUri = in.readString();
|
2009-06-16 19:03:45 +00:00
|
|
|
mMessageKey = in.readLong();
|
|
|
|
mLocation = in.readString();
|
|
|
|
mEncoding = in.readString();
|
2010-02-22 20:57:33 +00:00
|
|
|
mContent = in.readString();
|
|
|
|
mFlags = in.readInt();
|
2010-12-09 01:11:04 +00:00
|
|
|
mAccountKey = in.readLong();
|
2010-03-12 21:30:26 +00:00
|
|
|
final int contentBytesLen = in.readInt();
|
|
|
|
if (contentBytesLen == -1) {
|
|
|
|
mContentBytes = null;
|
|
|
|
} else {
|
|
|
|
mContentBytes = new byte[contentBytesLen];
|
|
|
|
in.readByteArray(mContentBytes);
|
|
|
|
}
|
2012-06-28 17:40:46 +00:00
|
|
|
mUiState = in.readInt();
|
|
|
|
mUiDestination = in.readInt();
|
|
|
|
mUiDownloadedSize = in.readInt();
|
2009-06-16 19:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static final Parcelable.Creator<EmailContent.Attachment> CREATOR
|
2011-05-06 18:20:37 +00:00
|
|
|
= new Parcelable.Creator<EmailContent.Attachment>() {
|
2011-05-12 17:13:45 +00:00
|
|
|
@Override
|
2009-06-16 19:03:45 +00:00
|
|
|
public EmailContent.Attachment createFromParcel(Parcel in) {
|
|
|
|
return new EmailContent.Attachment(in);
|
|
|
|
}
|
|
|
|
|
2011-05-12 17:13:45 +00:00
|
|
|
@Override
|
2009-06-16 19:03:45 +00:00
|
|
|
public EmailContent.Attachment[] newArray(int size) {
|
|
|
|
return new EmailContent.Attachment[size];
|
|
|
|
}
|
|
|
|
};
|
2009-10-14 15:20:59 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return "[" + mFileName + ", " + mMimeType + ", " + mSize + ", " + mContentId + ", "
|
2013-03-21 23:54:49 +00:00
|
|
|
+ mContentUri + ", " + mCachedFileUri + ", " + mMessageKey + ", "
|
2013-02-23 03:42:40 +00:00
|
|
|
+ mLocation + ", " + mEncoding + ", " + mFlags + ", " + mContentBytes + ", "
|
|
|
|
+ mAccountKey + "," + mUiState + "," + mUiDestination + ","
|
|
|
|
+ mUiDownloadedSize + "]";
|
2009-10-14 15:20:59 +00:00
|
|
|
}
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface AccountColumns extends BaseColumns {
|
2011-06-13 22:32:27 +00:00
|
|
|
// The display name of the account (user-settable)
|
|
|
|
public static final String DISPLAY_NAME = "displayName";
|
|
|
|
// The email address corresponding to this account
|
|
|
|
public static final String EMAIL_ADDRESS = "emailAddress";
|
|
|
|
// A server-based sync key on an account-wide basis (EAS needs this)
|
|
|
|
public static final String SYNC_KEY = "syncKey";
|
|
|
|
// The default sync lookback period for this account
|
|
|
|
public static final String SYNC_LOOKBACK = "syncLookback";
|
|
|
|
// The default sync frequency for this account, in minutes
|
|
|
|
public static final String SYNC_INTERVAL = "syncInterval";
|
|
|
|
// A foreign key into the account manager, having host, login, password, port, and ssl flags
|
|
|
|
public static final String HOST_AUTH_KEY_RECV = "hostAuthKeyRecv";
|
|
|
|
// (optional) A foreign key into the account manager, having host, login, password, port,
|
|
|
|
// and ssl flags
|
|
|
|
public static final String HOST_AUTH_KEY_SEND = "hostAuthKeySend";
|
|
|
|
// Flags
|
|
|
|
public static final String FLAGS = "flags";
|
2013-06-26 22:49:12 +00:00
|
|
|
/**
|
|
|
|
* Default account
|
|
|
|
*
|
|
|
|
* @deprecated This should never be used any more, as default accounts are handled
|
|
|
|
* differently now
|
|
|
|
*/
|
|
|
|
@Deprecated
|
2011-06-13 22:32:27 +00:00
|
|
|
public static final String IS_DEFAULT = "isDefault";
|
|
|
|
// Old-Style UUID for compatibility with previous versions
|
2014-05-13 17:46:30 +00:00
|
|
|
@Deprecated
|
2011-06-13 22:32:27 +00:00
|
|
|
public static final String COMPATIBILITY_UUID = "compatibilityUuid";
|
|
|
|
// User name (for outgoing messages)
|
|
|
|
public static final String SENDER_NAME = "senderName";
|
2012-12-11 18:37:35 +00:00
|
|
|
/**
|
|
|
|
* Ringtone
|
|
|
|
*
|
2013-08-26 19:21:13 +00:00
|
|
|
* @deprecated Only used for creating the database (legacy reasons) and migration.
|
2012-12-11 18:37:35 +00:00
|
|
|
*/
|
|
|
|
@Deprecated
|
2011-06-13 22:32:27 +00:00
|
|
|
public static final String RINGTONE_URI = "ringtoneUri";
|
|
|
|
// Protocol version (arbitrary string, used by EAS currently)
|
|
|
|
public static final String PROTOCOL_VERSION = "protocolVersion";
|
|
|
|
// The number of new messages (reported by the sync/download engines
|
2014-05-14 18:16:56 +00:00
|
|
|
@Deprecated
|
2011-06-13 22:32:27 +00:00
|
|
|
public static final String NEW_MESSAGE_COUNT = "newMessageCount";
|
|
|
|
// Legacy flags defining security (provisioning) requirements of this account; this
|
|
|
|
// information is now found in the Policy table; POLICY_KEY (below) is the foreign key
|
|
|
|
@Deprecated
|
|
|
|
public static final String SECURITY_FLAGS = "securityFlags";
|
|
|
|
// Server-based sync key for the security policies currently enforced
|
|
|
|
public static final String SECURITY_SYNC_KEY = "securitySyncKey";
|
|
|
|
// Signature to use with this account
|
|
|
|
public static final String SIGNATURE = "signature";
|
|
|
|
// A foreign key into the Policy table
|
|
|
|
public static final String POLICY_KEY = "policyKey";
|
2014-03-06 23:27:54 +00:00
|
|
|
// Max upload attachment size.
|
|
|
|
public static final String MAX_ATTACHMENT_SIZE = "maxAttachmentSize";
|
2013-08-21 17:46:55 +00:00
|
|
|
// Current duration of the Exchange ping
|
|
|
|
public static final String PING_DURATION = "pingDuration";
|
2011-06-13 22:32:27 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface QuickResponseColumns extends BaseColumns {
|
2011-06-01 17:09:26 +00:00
|
|
|
// The QuickResponse text
|
|
|
|
static final String TEXT = "quickResponse";
|
|
|
|
// A foreign key into the Account table owning the QuickResponse
|
|
|
|
static final String ACCOUNT_KEY = "accountKey";
|
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface MailboxColumns extends BaseColumns {
|
|
|
|
// Use _ID instead
|
|
|
|
@Deprecated
|
2009-06-15 21:40:06 +00:00
|
|
|
public static final String ID = "_id";
|
|
|
|
// The display name of this mailbox [INDEX]
|
2009-07-30 18:41:31 +00:00
|
|
|
static final String DISPLAY_NAME = "displayName";
|
2009-06-15 21:40:06 +00:00
|
|
|
// The server's identifier for this mailbox
|
|
|
|
public static final String SERVER_ID = "serverId";
|
|
|
|
// The server's identifier for the parent of this mailbox (null = top-level)
|
|
|
|
public static final String PARENT_SERVER_ID = "parentServerId";
|
2011-03-01 23:29:45 +00:00
|
|
|
// A foreign key for the parent of this mailbox (-1 = top-level, 0=uninitialized)
|
|
|
|
public static final String PARENT_KEY = "parentKey";
|
2009-06-15 21:40:06 +00:00
|
|
|
// A foreign key to the Account that owns this mailbox
|
|
|
|
public static final String ACCOUNT_KEY = "accountKey";
|
|
|
|
// The type (role) of this mailbox
|
|
|
|
public static final String TYPE = "type";
|
|
|
|
// The hierarchy separator character
|
|
|
|
public static final String DELIMITER = "delimiter";
|
|
|
|
// Server-based sync key or validity marker (e.g. "SyncKey" for EAS, "uidvalidity" for IMAP)
|
|
|
|
public static final String SYNC_KEY = "syncKey";
|
|
|
|
// The sync lookback period for this mailbox (or null if using the account default)
|
|
|
|
public static final String SYNC_LOOKBACK = "syncLookback";
|
|
|
|
// The sync frequency for this mailbox (or null if using the account default)
|
2009-07-22 22:13:30 +00:00
|
|
|
public static final String SYNC_INTERVAL = "syncInterval";
|
2009-06-15 21:40:06 +00:00
|
|
|
// The time of last successful sync completion (millis)
|
|
|
|
public static final String SYNC_TIME = "syncTime";
|
|
|
|
// Cached unread count
|
|
|
|
public static final String UNREAD_COUNT = "unreadCount";
|
|
|
|
// Visibility of this folder in a list of folders [INDEX]
|
|
|
|
public static final String FLAG_VISIBLE = "flagVisible";
|
|
|
|
// Other states, as a bit field, e.g. CHILDREN_VISIBLE, HAS_CHILDREN
|
|
|
|
public static final String FLAGS = "flags";
|
|
|
|
// Backward compatible
|
2013-04-05 01:30:39 +00:00
|
|
|
@Deprecated
|
2009-06-15 21:40:06 +00:00
|
|
|
public static final String VISIBLE_LIMIT = "visibleLimit";
|
2009-08-05 15:41:16 +00:00
|
|
|
// Sync status (can be used as desired by sync services)
|
|
|
|
public static final String SYNC_STATUS = "syncStatus";
|
2013-07-31 03:10:36 +00:00
|
|
|
// Number of messages locally available in the mailbox.
|
2010-07-30 20:53:59 +00:00
|
|
|
public static final String MESSAGE_COUNT = "messageCount";
|
2011-06-03 15:51:25 +00:00
|
|
|
// The last time a message in this mailbox has been read (in millis)
|
|
|
|
public static final String LAST_TOUCHED_TIME = "lastTouchedTime";
|
2012-06-28 17:40:46 +00:00
|
|
|
// The UIProvider sync status
|
|
|
|
public static final String UI_SYNC_STATUS = "uiSyncStatus";
|
|
|
|
// The UIProvider last sync result
|
|
|
|
public static final String UI_LAST_SYNC_RESULT = "uiLastSyncResult";
|
2012-12-11 18:37:35 +00:00
|
|
|
/**
|
|
|
|
* The UIProvider sync status
|
|
|
|
*
|
|
|
|
* @deprecated This is no longer used by anything except for creating the database.
|
|
|
|
*/
|
|
|
|
@Deprecated
|
2012-06-28 17:40:46 +00:00
|
|
|
public static final String LAST_NOTIFIED_MESSAGE_KEY = "lastNotifiedMessageKey";
|
2012-12-11 18:37:35 +00:00
|
|
|
/**
|
|
|
|
* The UIProvider last sync result
|
|
|
|
*
|
|
|
|
* @deprecated This is no longer used by anything except for creating the database.
|
|
|
|
*/
|
|
|
|
@Deprecated
|
2012-06-28 17:40:46 +00:00
|
|
|
public static final String LAST_NOTIFIED_MESSAGE_COUNT = "lastNotifiedMessageCount";
|
|
|
|
// The total number of messages in the remote mailbox
|
|
|
|
public static final String TOTAL_COUNT = "totalCount";
|
|
|
|
// The full hierarchical name of this folder, in the form a/b/c
|
|
|
|
public static final String HIERARCHICAL_NAME = "hierarchicalName";
|
2013-08-06 23:50:11 +00:00
|
|
|
// The last time that we did a full sync. Set from SystemClock.elapsedRealtime().
|
|
|
|
public static final String LAST_FULL_SYNC_TIME = "lastFullSyncTime";
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface HostAuthColumns extends BaseColumns {
|
2009-06-15 21:40:06 +00:00
|
|
|
// The protocol (e.g. "imap", "pop3", "eas", "smtp"
|
|
|
|
static final String PROTOCOL = "protocol";
|
|
|
|
// The host address
|
2009-07-30 18:41:31 +00:00
|
|
|
static final String ADDRESS = "address";
|
2009-06-15 21:40:06 +00:00
|
|
|
// The port to use for the connection
|
2009-07-30 18:41:31 +00:00
|
|
|
static final String PORT = "port";
|
2009-06-15 21:40:06 +00:00
|
|
|
// General purpose flags
|
2009-07-30 18:41:31 +00:00
|
|
|
static final String FLAGS = "flags";
|
2009-06-15 21:40:06 +00:00
|
|
|
// The login (user name)
|
2009-07-30 18:41:31 +00:00
|
|
|
static final String LOGIN = "login";
|
2009-06-15 21:40:06 +00:00
|
|
|
// Password
|
2009-07-30 18:41:31 +00:00
|
|
|
static final String PASSWORD = "password";
|
2009-06-15 21:40:06 +00:00
|
|
|
// A domain or path, if required (used in IMAP and EAS)
|
2009-07-30 18:41:31 +00:00
|
|
|
static final String DOMAIN = "domain";
|
2011-06-07 18:39:16 +00:00
|
|
|
// An alias to a local client certificate for SSL
|
|
|
|
static final String CLIENT_CERT_ALIAS = "certAlias";
|
2009-09-15 21:10:12 +00:00
|
|
|
// DEPRECATED - Will not be set or stored
|
2009-06-15 21:40:06 +00:00
|
|
|
static final String ACCOUNT_KEY = "accountKey";
|
2012-07-31 22:47:49 +00:00
|
|
|
// A blob containing an X509 server certificate
|
|
|
|
static final String SERVER_CERT = "serverCert";
|
2013-12-04 00:55:48 +00:00
|
|
|
// The credentials row this hostAuth should use. Currently only set if using OAuth.
|
|
|
|
static final String CREDENTIAL_KEY = "credentialKey";
|
2009-06-15 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-11 21:42:28 +00:00
|
|
|
public interface PolicyColumns extends BaseColumns {
|
2011-04-28 00:12:06 +00:00
|
|
|
public static final String PASSWORD_MODE = "passwordMode";
|
|
|
|
public static final String PASSWORD_MIN_LENGTH = "passwordMinLength";
|
|
|
|
public static final String PASSWORD_EXPIRATION_DAYS = "passwordExpirationDays";
|
|
|
|
public static final String PASSWORD_HISTORY = "passwordHistory";
|
|
|
|
public static final String PASSWORD_COMPLEX_CHARS = "passwordComplexChars";
|
|
|
|
public static final String PASSWORD_MAX_FAILS = "passwordMaxFails";
|
|
|
|
public static final String MAX_SCREEN_LOCK_TIME = "maxScreenLockTime";
|
|
|
|
public static final String REQUIRE_REMOTE_WIPE = "requireRemoteWipe";
|
|
|
|
public static final String REQUIRE_ENCRYPTION = "requireEncryption";
|
|
|
|
public static final String REQUIRE_ENCRYPTION_EXTERNAL = "requireEncryptionExternal";
|
2011-05-05 17:34:29 +00:00
|
|
|
// ICS additions
|
|
|
|
// Note: the appearance of these columns does not imply that we support these features; only
|
|
|
|
// that we store them in the Policy structure
|
|
|
|
public static final String REQUIRE_MANUAL_SYNC_WHEN_ROAMING = "requireManualSyncRoaming";
|
|
|
|
public static final String DONT_ALLOW_CAMERA = "dontAllowCamera";
|
|
|
|
public static final String DONT_ALLOW_ATTACHMENTS = "dontAllowAttachments";
|
|
|
|
public static final String DONT_ALLOW_HTML = "dontAllowHtml";
|
|
|
|
public static final String MAX_ATTACHMENT_SIZE = "maxAttachmentSize";
|
|
|
|
public static final String MAX_TEXT_TRUNCATION_SIZE = "maxTextTruncationSize";
|
|
|
|
public static final String MAX_HTML_TRUNCATION_SIZE = "maxHTMLTruncationSize";
|
|
|
|
public static final String MAX_EMAIL_LOOKBACK = "maxEmailLookback";
|
|
|
|
public static final String MAX_CALENDAR_LOOKBACK = "maxCalendarLookback";
|
|
|
|
// Indicates that the server allows password recovery, not that we support it
|
|
|
|
public static final String PASSWORD_RECOVERY_ENABLED = "passwordRecoveryEnabled";
|
2012-06-28 17:40:46 +00:00
|
|
|
// Tokenized strings indicating protocol specific policies enforced/unsupported
|
|
|
|
public static final String PROTOCOL_POLICIES_ENFORCED = "protocolPoliciesEnforced";
|
|
|
|
public static final String PROTOCOL_POLICIES_UNSUPPORTED = "protocolPoliciesUnsupported";
|
2011-04-28 00:12:06 +00:00
|
|
|
}
|
2009-08-12 10:51:26 +00:00
|
|
|
}
|