243 lines
11 KiB
Java
243 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.email.provider;
|
|
|
|
import android.content.ContentResolver;
|
|
import android.content.ContentValues;
|
|
import android.content.Context;
|
|
import android.database.Cursor;
|
|
import android.database.CursorWrapper;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.text.TextUtils;
|
|
import android.text.format.DateUtils;
|
|
import android.text.util.Rfc822Token;
|
|
import android.text.util.Rfc822Tokenizer;
|
|
|
|
import com.android.emailcommon.Logging;
|
|
import com.android.emailcommon.mail.Address;
|
|
import com.android.emailcommon.provider.EmailContent;
|
|
import com.android.emailcommon.provider.Mailbox;
|
|
import com.android.mail.browse.ConversationCursorOperationListener;
|
|
import com.android.mail.providers.ConversationInfo;
|
|
import com.android.mail.providers.Folder;
|
|
import com.android.mail.providers.FolderList;
|
|
import com.android.mail.providers.ParticipantInfo;
|
|
import com.android.mail.providers.UIProvider;
|
|
import com.android.mail.providers.UIProvider.ConversationColumns;
|
|
import com.android.mail.utils.LogUtils;
|
|
import com.google.common.collect.Lists;
|
|
|
|
/**
|
|
* Wrapper that handles the visibility feature (i.e. the conversation list is visible, so
|
|
* any pending notifications for the corresponding mailbox should be canceled). We also handle
|
|
* getExtras() to provide a snapshot of the mailbox's status
|
|
*/
|
|
public class EmailConversationCursor extends CursorWrapper implements
|
|
ConversationCursorOperationListener {
|
|
private final long mMailboxId;
|
|
private final int mMailboxTypeId;
|
|
private final Context mContext;
|
|
private final FolderList mFolderList;
|
|
private final Bundle mExtras = new Bundle();
|
|
|
|
/**
|
|
* When showing a folder, if it's been at least this long since the last sync,
|
|
* force a folder refresh.
|
|
*/
|
|
private static final long AUTO_REFRESH_INTERVAL_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
|
|
|
|
public EmailConversationCursor(final Context context, final Cursor cursor,
|
|
final Folder folder, final long mailboxId) {
|
|
super(cursor);
|
|
mMailboxId = mailboxId;
|
|
mContext = context;
|
|
mFolderList = FolderList.copyOf(Lists.newArrayList(folder));
|
|
Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
|
|
|
|
if (mailbox != null) {
|
|
mMailboxTypeId = mailbox.mType;
|
|
|
|
mExtras.putInt(UIProvider.CursorExtraKeys.EXTRA_TOTAL_COUNT, mailbox.mTotalCount);
|
|
if (mailbox.mUiSyncStatus == EmailContent.SYNC_STATUS_BACKGROUND
|
|
|| mailbox.mUiSyncStatus == EmailContent.SYNC_STATUS_USER
|
|
|| mailbox.mUiSyncStatus == EmailContent.SYNC_STATUS_LIVE
|
|
|| mailbox.mUiSyncStatus == EmailContent.SYNC_STATUS_INITIAL_SYNC_NEEDED) {
|
|
mExtras.putInt(UIProvider.CursorExtraKeys.EXTRA_STATUS,
|
|
UIProvider.CursorStatus.LOADING);
|
|
} else if (mailbox.mUiSyncStatus == EmailContent.SYNC_STATUS_NONE) {
|
|
if (mailbox.mSyncInterval == 0
|
|
&& (Mailbox.isSyncableType(mailbox.mType)
|
|
|| mailbox.mType == Mailbox.TYPE_SEARCH)
|
|
&& !TextUtils.isEmpty(mailbox.mServerId) &&
|
|
// TODO: There's potentially a race condition here.
|
|
// Consider merging this check with the auto-sync code in respond.
|
|
System.currentTimeMillis() - mailbox.mSyncTime
|
|
> AUTO_REFRESH_INTERVAL_MS) {
|
|
// This will be syncing momentarily
|
|
mExtras.putInt(UIProvider.CursorExtraKeys.EXTRA_STATUS,
|
|
UIProvider.CursorStatus.LOADING);
|
|
} else {
|
|
mExtras.putInt(UIProvider.CursorExtraKeys.EXTRA_STATUS,
|
|
UIProvider.CursorStatus.COMPLETE);
|
|
}
|
|
} else {
|
|
LogUtils.d(Logging.LOG_TAG,
|
|
"Unknown mailbox sync status" + mailbox.mUiSyncStatus);
|
|
mExtras.putInt(UIProvider.CursorExtraKeys.EXTRA_STATUS,
|
|
UIProvider.CursorStatus.COMPLETE);
|
|
}
|
|
} else {
|
|
mMailboxTypeId = -1;
|
|
// TODO for virtual mailboxes, we may want to do something besides just fake it
|
|
mExtras.putInt(UIProvider.CursorExtraKeys.EXTRA_TOTAL_COUNT,
|
|
cursor != null ? cursor.getCount() : 0);
|
|
mExtras.putInt(UIProvider.CursorExtraKeys.EXTRA_STATUS,
|
|
UIProvider.CursorStatus.COMPLETE);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Bundle getExtras() {
|
|
return mExtras;
|
|
}
|
|
|
|
@Override
|
|
public Bundle respond(Bundle params) {
|
|
final String setVisibilityKey =
|
|
UIProvider.ConversationCursorCommand.COMMAND_KEY_SET_VISIBILITY;
|
|
if (params.containsKey(setVisibilityKey)) {
|
|
final boolean visible = params.getBoolean(setVisibilityKey);
|
|
if (visible) {
|
|
// Mark all messages as seen
|
|
markContentsSeen();
|
|
if (params.containsKey(
|
|
UIProvider.ConversationCursorCommand.COMMAND_KEY_ENTERED_FOLDER)) {
|
|
Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mMailboxId);
|
|
if (mailbox != null) {
|
|
// For non-push mailboxes, if it's stale (i.e. last sync was a while
|
|
// ago), force a sync.
|
|
// TODO: Fix the check for whether we're non-push? Right now it checks
|
|
// whether we are participating in account sync rules.
|
|
if (mailbox.mSyncInterval == 0) {
|
|
final long timeSinceLastSync =
|
|
System.currentTimeMillis() - mailbox.mSyncTime;
|
|
if (timeSinceLastSync > AUTO_REFRESH_INTERVAL_MS) {
|
|
final ContentResolver resolver = mContext.getContentResolver();
|
|
final Uri refreshUri = Uri.parse(EmailContent.CONTENT_URI +
|
|
"/" + EmailProvider.QUERY_UIREFRESH + "/" + mailbox.mId);
|
|
resolver.query(refreshUri, null, null, null, null);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Return success
|
|
final Bundle response = new Bundle(2);
|
|
|
|
response.putString(setVisibilityKey,
|
|
UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_OK);
|
|
|
|
final String rawFoldersKey =
|
|
UIProvider.ConversationCursorCommand.COMMAND_GET_RAW_FOLDERS;
|
|
if (params.containsKey(rawFoldersKey)) {
|
|
response.putParcelable(rawFoldersKey, mFolderList);
|
|
}
|
|
|
|
final String convInfoKey =
|
|
UIProvider.ConversationCursorCommand.COMMAND_GET_CONVERSATION_INFO;
|
|
if (params.containsKey(convInfoKey)) {
|
|
response.putParcelable(convInfoKey, generateConversationInfo());
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
private ConversationInfo generateConversationInfo() {
|
|
final int numMessages = getInt(getColumnIndex(ConversationColumns.NUM_MESSAGES));
|
|
final ConversationInfo conversationInfo = new ConversationInfo(numMessages);
|
|
|
|
conversationInfo.firstSnippet = getString(getColumnIndex(ConversationColumns.SNIPPET));
|
|
conversationInfo.lastSnippet = conversationInfo.firstSnippet;
|
|
conversationInfo.firstUnreadSnippet = conversationInfo.firstSnippet;
|
|
|
|
final boolean isRead = getInt(getColumnIndex(ConversationColumns.READ)) != 0;
|
|
final String senderString = getString(getColumnIndex(EmailContent.MessageColumns.DISPLAY_NAME));
|
|
|
|
final String fromString = getString(getColumnIndex(EmailContent.MessageColumns.FROM_LIST));
|
|
final String senderEmail;
|
|
|
|
if (fromString != null) {
|
|
final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(fromString);
|
|
if (tokens.length > 0) {
|
|
senderEmail = tokens[0].getAddress();
|
|
} else {
|
|
LogUtils.d(LogUtils.TAG, "Couldn't parse sender email address");
|
|
senderEmail = fromString;
|
|
}
|
|
} else {
|
|
senderEmail = null;
|
|
}
|
|
|
|
// we *intentionally* report no participants for Draft emails so that the UI always
|
|
// displays the single word "Draft" as per b/13304929
|
|
if (mMailboxTypeId == Mailbox.TYPE_DRAFTS) {
|
|
// the UI displays "Draft" in the conversation list based on this count
|
|
conversationInfo.draftCount = 1;
|
|
} else if (mMailboxTypeId == Mailbox.TYPE_SENT ||
|
|
mMailboxTypeId == Mailbox.TYPE_OUTBOX) {
|
|
// for conversations in outgoing mail mailboxes return a list of recipients
|
|
final String recipientsString = getString(getColumnIndex(
|
|
EmailContent.MessageColumns.TO_LIST));
|
|
final Address[] recipientAddresses = Address.parse(recipientsString);
|
|
for (Address recipientAddress : recipientAddresses) {
|
|
final String name = recipientAddress.getSimplifiedName();
|
|
final String email = recipientAddress.getAddress();
|
|
|
|
// all recipients are said to have read all messages in the conversation
|
|
conversationInfo.addParticipant(new ParticipantInfo(name, email, 0, isRead));
|
|
}
|
|
} else {
|
|
// for conversations in incoming mail mailboxes return the sender
|
|
conversationInfo.addParticipant(new ParticipantInfo(senderString, senderEmail, 0,
|
|
isRead));
|
|
}
|
|
|
|
return conversationInfo;
|
|
}
|
|
|
|
@Override
|
|
public void markContentsSeen() {
|
|
final ContentResolver resolver = mContext.getContentResolver();
|
|
final ContentValues contentValues = new ContentValues(1);
|
|
contentValues.put(EmailContent.MessageColumns.FLAG_SEEN, true);
|
|
final Uri uri = EmailContent.Message.CONTENT_URI;
|
|
final String where = EmailContent.MessageColumns.MAILBOX_KEY + " = ? AND " +
|
|
EmailContent.MessageColumns.FLAG_SEEN + " != ?";
|
|
final String[] selectionArgs = {String.valueOf(mMailboxId), "1"};
|
|
resolver.update(uri, contentValues, where, selectionArgs);
|
|
}
|
|
|
|
@Override
|
|
public void emptyFolder() {
|
|
final ContentResolver resolver = mContext.getContentResolver();
|
|
final Uri purgeUri = EmailProvider.uiUri("uipurgefolder", mMailboxId);
|
|
resolver.delete(purgeUri, null, null);
|
|
}
|
|
}
|