Remove notification if messages seen off device
If we receive new messages, we may display a notification to the user. If those same messages are read elsewhere (i.e. via a web client), we will remove the notification. Change-Id: Iba09afe01942e0deaac8210fd6f9b315b1c8c93f
This commit is contained in:
parent
c96cd4a848
commit
c4cdb11d24
@ -54,6 +54,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermedia
|
|||||||
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
|
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
|
||||||
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
|
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
|
||||||
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
|
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
|
||||||
|
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
|
||||||
|
|
||||||
# ************************************************
|
# ************************************************
|
||||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
||||||
|
@ -97,6 +97,7 @@ public abstract class EmailContent {
|
|||||||
|
|
||||||
public static final String FIELD_COLUMN_NAME = "field";
|
public static final String FIELD_COLUMN_NAME = "field";
|
||||||
public static final String ADD_COLUMN_NAME = "add";
|
public static final String ADD_COLUMN_NAME = "add";
|
||||||
|
public static final String SET_COLUMN_NAME = "set";
|
||||||
|
|
||||||
// Newly created objects get this id
|
// Newly created objects get this id
|
||||||
public static final int NOT_SAVED = -1;
|
public static final int NOT_SAVED = -1;
|
||||||
|
@ -22,6 +22,8 @@ import android.os.Bundle;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class AccountServiceProxy extends ServiceProxy implements IAccountService {
|
public class AccountServiceProxy extends ServiceProxy implements IAccountService {
|
||||||
|
|
||||||
public static final String ACCOUNT_INTENT = "com.android.email.ACCOUNT_INTENT";
|
public static final String ACCOUNT_INTENT = "com.android.email.ACCOUNT_INTENT";
|
||||||
@ -44,7 +46,7 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyLoginFailed(final long accountId) throws RemoteException {
|
public void notifyLoginFailed(final long accountId) {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException {
|
public void run() throws RemoteException {
|
||||||
mService.notifyLoginFailed(accountId);
|
mService.notifyLoginFailed(accountId);
|
||||||
@ -53,7 +55,7 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyLoginSucceeded(final long accountId) throws RemoteException {
|
public void notifyLoginSucceeded(final long accountId) {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException {
|
public void run() throws RemoteException {
|
||||||
mService.notifyLoginSucceeded(accountId);
|
mService.notifyLoginSucceeded(accountId);
|
||||||
@ -62,16 +64,17 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyNewMessages(final long accountId) throws RemoteException {
|
@SuppressWarnings("unchecked")
|
||||||
|
public void notifyNewMessages(final long accountId, final List messageIdList) {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException {
|
public void run() throws RemoteException {
|
||||||
mService.notifyNewMessages(accountId);
|
mService.notifyNewMessages(accountId, messageIdList);
|
||||||
}
|
}
|
||||||
}, "notifyNewMessages");
|
}, "notifyNewMessages");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accountDeleted() throws RemoteException {
|
public void accountDeleted() {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException {
|
public void run() throws RemoteException {
|
||||||
mService.accountDeleted();
|
mService.accountDeleted();
|
||||||
@ -81,7 +84,7 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
|
|||||||
|
|
||||||
// The following call is synchronous, and should not be made from the UI thread
|
// The following call is synchronous, and should not be made from the UI thread
|
||||||
@Override
|
@Override
|
||||||
public void restoreAccountsIfNeeded() throws RemoteException {
|
public void restoreAccountsIfNeeded() {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException {
|
public void run() throws RemoteException {
|
||||||
mService.restoreAccountsIfNeeded();
|
mService.restoreAccountsIfNeeded();
|
||||||
@ -92,7 +95,7 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
|
|||||||
|
|
||||||
// The following call is synchronous, and should not be made from the UI thread
|
// The following call is synchronous, and should not be made from the UI thread
|
||||||
@Override
|
@Override
|
||||||
public int getAccountColor(final long accountId) throws RemoteException {
|
public int getAccountColor(final long accountId) {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException{
|
public void run() throws RemoteException{
|
||||||
mReturn = mService.getAccountColor(accountId);
|
mReturn = mService.getAccountColor(accountId);
|
||||||
@ -107,7 +110,7 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The following call is synchronous, and should not be made from the UI thread
|
// The following call is synchronous, and should not be made from the UI thread
|
||||||
public Bundle getConfigurationData(final String accountType) throws RemoteException {
|
public Bundle getConfigurationData(final String accountType) {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException{
|
public void run() throws RemoteException{
|
||||||
mReturn = mService.getConfigurationData(accountType);
|
mReturn = mService.getConfigurationData(accountType);
|
||||||
@ -122,7 +125,7 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The following call is synchronous, and should not be made from the UI thread
|
// The following call is synchronous, and should not be made from the UI thread
|
||||||
public String getDeviceId() throws RemoteException {
|
public String getDeviceId() {
|
||||||
setTask(new ProxyTask() {
|
setTask(new ProxyTask() {
|
||||||
public void run() throws RemoteException{
|
public void run() throws RemoteException{
|
||||||
mReturn = mService.getDeviceId();
|
mReturn = mService.getDeviceId();
|
||||||
|
@ -21,7 +21,7 @@ import android.os.Bundle;
|
|||||||
interface IAccountService {
|
interface IAccountService {
|
||||||
oneway void notifyLoginFailed(long accountId);
|
oneway void notifyLoginFailed(long accountId);
|
||||||
oneway void notifyLoginSucceeded(long accountId);
|
oneway void notifyLoginSucceeded(long accountId);
|
||||||
oneway void notifyNewMessages(long accountId);
|
oneway void notifyNewMessages(long accountId, in List messageIdList);
|
||||||
|
|
||||||
void accountDeleted();
|
void accountDeleted();
|
||||||
void restoreAccountsIfNeeded();
|
void restoreAccountsIfNeeded();
|
||||||
|
@ -58,6 +58,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.InvalidParameterException;
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -1142,7 +1143,7 @@ public class Controller {
|
|||||||
* @param numNewMessages the number of new messages delivered
|
* @param numNewMessages the number of new messages delivered
|
||||||
*/
|
*/
|
||||||
public void updateMailboxCallback(MessagingException result, long accountId,
|
public void updateMailboxCallback(MessagingException result, long accountId,
|
||||||
long mailboxId, int progress, int numNewMessages) {
|
long mailboxId, int progress, int numNewMessages, ArrayList<Long> addedMessages) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1292,17 +1293,18 @@ public class Controller {
|
|||||||
public void synchronizeMailboxStarted(long accountId, long mailboxId) {
|
public void synchronizeMailboxStarted(long accountId, long mailboxId) {
|
||||||
synchronized (mListeners) {
|
synchronized (mListeners) {
|
||||||
for (Result l : mListeners) {
|
for (Result l : mListeners) {
|
||||||
l.updateMailboxCallback(null, accountId, mailboxId, 0, 0);
|
l.updateMailboxCallback(null, accountId, mailboxId, 0, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void synchronizeMailboxFinished(long accountId, long mailboxId,
|
public void synchronizeMailboxFinished(long accountId, long mailboxId,
|
||||||
int totalMessagesInMailbox, int numNewMessages) {
|
int totalMessagesInMailbox, int numNewMessages, ArrayList<Long> addedMessages) {
|
||||||
synchronized (mListeners) {
|
synchronized (mListeners) {
|
||||||
for (Result l : mListeners) {
|
for (Result l : mListeners) {
|
||||||
l.updateMailboxCallback(null, accountId, mailboxId, 100, numNewMessages);
|
l.updateMailboxCallback(null, accountId, mailboxId, 100, numNewMessages,
|
||||||
|
addedMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1317,7 +1319,7 @@ public class Controller {
|
|||||||
}
|
}
|
||||||
synchronized (mListeners) {
|
synchronized (mListeners) {
|
||||||
for (Result l : mListeners) {
|
for (Result l : mListeners) {
|
||||||
l.updateMailboxCallback(me, accountId, mailboxId, 0, 0);
|
l.updateMailboxCallback(me, accountId, mailboxId, 0, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1566,7 +1568,7 @@ public class Controller {
|
|||||||
long accountId = mbx.mAccountKey;
|
long accountId = mbx.mAccountKey;
|
||||||
synchronized(mListeners) {
|
synchronized(mListeners) {
|
||||||
for (Result listener : mListeners) {
|
for (Result listener : mListeners) {
|
||||||
listener.updateMailboxCallback(result, accountId, mailboxId, progress, 0);
|
listener.updateMailboxCallback(result, accountId, mailboxId, progress, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ package com.android.email;
|
|||||||
import com.android.email.Controller.Result;
|
import com.android.email.Controller.Result;
|
||||||
import com.android.emailcommon.mail.MessagingException;
|
import com.android.emailcommon.mail.MessagingException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,12 +108,13 @@ public class ControllerResultUiThreadWrapper<T extends Result> extends Result {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMailboxCallback(final MessagingException result, final long accountId,
|
public void updateMailboxCallback(final MessagingException result, final long accountId,
|
||||||
final long mailboxId, final int progress, final int numNewMessages) {
|
final long mailboxId, final int progress, final int numNewMessages,
|
||||||
|
final ArrayList<Long> addedMessages) {
|
||||||
run(new Runnable() {
|
run(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!isRegistered()) return;
|
if (!isRegistered()) return;
|
||||||
mWrappee.updateMailboxCallback(result, accountId, mailboxId, progress,
|
mWrappee.updateMailboxCallback(result, accountId, mailboxId, progress,
|
||||||
numNewMessages);
|
numNewMessages, addedMessages);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import com.android.emailcommon.mail.MessagingException;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -80,10 +81,10 @@ public class GroupMessagingListener extends MessagingListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
synchronized public void synchronizeMailboxFinished(long accountId, long mailboxId,
|
synchronized public void synchronizeMailboxFinished(long accountId, long mailboxId,
|
||||||
int totalMessagesInMailbox, int numNewMessages) {
|
int totalMessagesInMailbox, int numNewMessages, ArrayList<Long> addedMessages) {
|
||||||
for (MessagingListener l : mListeners) {
|
for (MessagingListener l : mListeners) {
|
||||||
l.synchronizeMailboxFinished(accountId, mailboxId,
|
l.synchronizeMailboxFinished(accountId, mailboxId,
|
||||||
totalMessagesInMailbox, numNewMessages);
|
totalMessagesInMailbox, numNewMessages, addedMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ package com.android.email;
|
|||||||
|
|
||||||
import com.android.email.mail.Sender;
|
import com.android.email.mail.Sender;
|
||||||
import com.android.email.mail.Store;
|
import com.android.email.mail.Store;
|
||||||
import com.android.email.mail.StoreSynchronizer;
|
|
||||||
import com.android.emailcommon.Logging;
|
import com.android.emailcommon.Logging;
|
||||||
import com.android.emailcommon.internet.MimeBodyPart;
|
import com.android.emailcommon.internet.MimeBodyPart;
|
||||||
import com.android.emailcommon.internet.MimeHeader;
|
import com.android.emailcommon.internet.MimeHeader;
|
||||||
@ -330,20 +329,19 @@ public class MessagingController implements Runnable {
|
|||||||
mListeners.synchronizeMailboxStarted(account.mId, folder.mId);
|
mListeners.synchronizeMailboxStarted(account.mId, folder.mId);
|
||||||
if ((folder.mFlags & Mailbox.FLAG_HOLDS_MAIL) == 0) {
|
if ((folder.mFlags & Mailbox.FLAG_HOLDS_MAIL) == 0) {
|
||||||
// We don't hold messages, so, nothing to synchronize
|
// We don't hold messages, so, nothing to synchronize
|
||||||
mListeners.synchronizeMailboxFinished(account.mId, folder.mId, 0, 0);
|
mListeners.synchronizeMailboxFinished(account.mId, folder.mId, 0, 0, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NotificationController nc = NotificationController.getInstance(mContext);
|
NotificationController nc = NotificationController.getInstance(mContext);
|
||||||
try {
|
try {
|
||||||
processPendingActionsSynchronous(account);
|
processPendingActionsSynchronous(account);
|
||||||
|
|
||||||
StoreSynchronizer.SyncResults results;
|
|
||||||
|
|
||||||
// Select generic sync or store-specific sync
|
// Select generic sync or store-specific sync
|
||||||
results = synchronizeMailboxGeneric(account, folder);
|
SyncResults results = synchronizeMailboxGeneric(account, folder);
|
||||||
mListeners.synchronizeMailboxFinished(account.mId, folder.mId,
|
mListeners.synchronizeMailboxFinished(account.mId, folder.mId,
|
||||||
results.mTotalMessages,
|
results.mTotalMessages,
|
||||||
results.mNewMessages);
|
results.mAddedMessages.size(),
|
||||||
|
results.mAddedMessages);
|
||||||
// Clear authentication notification for this account
|
// Clear authentication notification for this account
|
||||||
nc.cancelLoginFailedNotification(account.mId);
|
nc.cancelLoginFailedNotification(account.mId);
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
@ -409,17 +407,23 @@ public class MessagingController implements Runnable {
|
|||||||
* @return results of the sync pass
|
* @return results of the sync pass
|
||||||
* @throws MessagingException
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
private StoreSynchronizer.SyncResults synchronizeMailboxGeneric(
|
private SyncResults synchronizeMailboxGeneric(
|
||||||
final EmailContent.Account account, final EmailContent.Mailbox folder)
|
final EmailContent.Account account, final EmailContent.Mailbox folder)
|
||||||
throws MessagingException {
|
throws MessagingException {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A list of IDs for messages that were downloaded and did not have the seen flag set.
|
||||||
|
* This serves as the "true" new message count reported to the user via notification.
|
||||||
|
*/
|
||||||
|
final ArrayList<Long> unseenMessages = new ArrayList<Long>();
|
||||||
|
|
||||||
Log.d(Logging.LOG_TAG, "*** synchronizeMailboxGeneric ***");
|
Log.d(Logging.LOG_TAG, "*** synchronizeMailboxGeneric ***");
|
||||||
ContentResolver resolver = mContext.getContentResolver();
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
|
||||||
// 0. We do not ever sync DRAFTS or OUTBOX (down or up)
|
// 0. We do not ever sync DRAFTS or OUTBOX (down or up)
|
||||||
if (folder.mType == Mailbox.TYPE_DRAFTS || folder.mType == Mailbox.TYPE_OUTBOX) {
|
if (folder.mType == Mailbox.TYPE_DRAFTS || folder.mType == Mailbox.TYPE_OUTBOX) {
|
||||||
int totalMessages = EmailContent.count(mContext, folder.getUri(), null, null);
|
int totalMessages = EmailContent.count(mContext, folder.getUri(), null, null);
|
||||||
return new StoreSynchronizer.SyncResults(totalMessages, 0);
|
return new SyncResults(totalMessages, unseenMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Get the message list from the local store and create an index of the uids
|
// 1. Get the message list from the local store and create an index of the uids
|
||||||
@ -474,7 +478,7 @@ public class MessagingController implements Runnable {
|
|||||||
|| folder.mType == Mailbox.TYPE_DRAFTS) {
|
|| folder.mType == Mailbox.TYPE_DRAFTS) {
|
||||||
if (!remoteFolder.exists()) {
|
if (!remoteFolder.exists()) {
|
||||||
if (!remoteFolder.create(FolderType.HOLDS_MESSAGES)) {
|
if (!remoteFolder.create(FolderType.HOLDS_MESSAGES)) {
|
||||||
return new StoreSynchronizer.SyncResults(0, 0);
|
return new SyncResults(0, unseenMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,13 +545,6 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 8. Download basic info about the new/unloaded messages (if any)
|
// 8. Download basic info about the new/unloaded messages (if any)
|
||||||
/*
|
|
||||||
* A list of messages that were downloaded and which did not have the Seen flag set.
|
|
||||||
* This will serve to indicate the true "new" message count that will be reported to
|
|
||||||
* the user via notification.
|
|
||||||
*/
|
|
||||||
final ArrayList<Message> newMessages = new ArrayList<Message>();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch the flags and envelope only of the new messages. This is intended to get us
|
* Fetch the flags and envelope only of the new messages. This is intended to get us
|
||||||
* critical data as fast as possible, and then we'll fill in the details.
|
* critical data as fast as possible, and then we'll fill in the details.
|
||||||
@ -584,7 +581,7 @@ public class MessagingController implements Runnable {
|
|||||||
saveOrUpdate(localMessage, mContext);
|
saveOrUpdate(localMessage, mContext);
|
||||||
// Track the "new" ness of the downloaded message
|
// Track the "new" ness of the downloaded message
|
||||||
if (!message.isSet(Flag.SEEN)) {
|
if (!message.isSet(Flag.SEEN)) {
|
||||||
newMessages.add(message);
|
unseenMessages.add(localMessage.mId);
|
||||||
}
|
}
|
||||||
} catch (MessagingException me) {
|
} catch (MessagingException me) {
|
||||||
Log.e(Logging.LOG_TAG,
|
Log.e(Logging.LOG_TAG,
|
||||||
@ -753,7 +750,7 @@ public class MessagingController implements Runnable {
|
|||||||
// 14. Clean up and report results
|
// 14. Clean up and report results
|
||||||
remoteFolder.close(false);
|
remoteFolder.close(false);
|
||||||
|
|
||||||
return new StoreSynchronizer.SyncResults(remoteMessageCount, newMessages.size());
|
return new SyncResults(remoteMessageCount, unseenMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1960,4 +1957,20 @@ public class MessagingController implements Runnable {
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Results of the latest synchronization. */
|
||||||
|
private static class SyncResults {
|
||||||
|
/** The total # of messages in the folder */
|
||||||
|
public final int mTotalMessages;
|
||||||
|
/** A list of new message IDs; must not be {@code null} */
|
||||||
|
public final ArrayList<Long> mAddedMessages;
|
||||||
|
|
||||||
|
public SyncResults(int totalMessages, ArrayList<Long> addedMessages) {
|
||||||
|
if (addedMessages == null) {
|
||||||
|
throw new IllegalArgumentException("addedMessages must not be null");
|
||||||
|
}
|
||||||
|
mTotalMessages = totalMessages;
|
||||||
|
mAddedMessages = addedMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ import com.android.emailcommon.mail.MessagingException;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the interface that MessagingController will use to callback to requesters. This class
|
* Defines the interface that MessagingController will use to callback to requesters. This class
|
||||||
* is defined as non-abstract so that someone who wants to receive only a few messages can
|
* is defined as non-abstract so that someone who wants to receive only a few messages can
|
||||||
@ -40,16 +42,25 @@ public class MessagingListener {
|
|||||||
public void listFoldersFinished(long accountId) {
|
public void listFoldersFinished(long accountId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void synchronizeMailboxStarted(long accountId, long mailboxId)
|
public void synchronizeMailboxStarted(long accountId, long mailboxId) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void synchronizeMailboxFinished(long accountId,
|
/**
|
||||||
long mailboxId, int totalMessagesInMailbox, int numNewMessages) {
|
* Synchronization of the mailbox finished. The mailbox and/or message databases have been
|
||||||
|
* updated accordingly.
|
||||||
|
*
|
||||||
|
* @param accountId The account that was synchronized
|
||||||
|
* @param mailboxId The mailbox that was synchronized
|
||||||
|
* @param totalMessagesInMailbox The total number of messages in the mailbox
|
||||||
|
* @param numNewMessages The number of new messages
|
||||||
|
* @param addedMessages Message IDs of messages that were added during the synchronization.
|
||||||
|
* These are new, unread messages. Messages that were previously read are not in this list.
|
||||||
|
*/
|
||||||
|
public void synchronizeMailboxFinished(long accountId, long mailboxId,
|
||||||
|
int totalMessagesInMailbox, int numNewMessages, ArrayList<Long> addedMessages) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void synchronizeMailboxFailed(long accountId, long mailboxId,
|
public void synchronizeMailboxFailed(long accountId, long mailboxId, Exception e) {
|
||||||
Exception e) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadMessageForViewStarted(long messageId) {
|
public void loadMessageForViewStarted(long messageId) {
|
||||||
|
@ -23,6 +23,7 @@ import com.android.email.activity.setup.AccountSettingsXL;
|
|||||||
import com.android.emailcommon.mail.Address;
|
import com.android.emailcommon.mail.Address;
|
||||||
import com.android.emailcommon.provider.EmailContent;
|
import com.android.emailcommon.provider.EmailContent;
|
||||||
import com.android.emailcommon.provider.EmailContent.Account;
|
import com.android.emailcommon.provider.EmailContent.Account;
|
||||||
|
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||||
import com.android.emailcommon.provider.EmailContent.Attachment;
|
import com.android.emailcommon.provider.EmailContent.Attachment;
|
||||||
import com.android.emailcommon.provider.EmailContent.Message;
|
import com.android.emailcommon.provider.EmailContent.Message;
|
||||||
import com.android.emailcommon.utility.EmailAsyncTask;
|
import com.android.emailcommon.utility.EmailAsyncTask;
|
||||||
@ -32,16 +33,26 @@ import com.google.common.annotations.VisibleForTesting;
|
|||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.database.ContentObserver;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.TextAppearanceSpan;
|
import android.text.style.TextAppearanceSpan;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that manages notifications.
|
* Class that manages notifications.
|
||||||
*/
|
*/
|
||||||
@ -63,6 +74,12 @@ public class NotificationController {
|
|||||||
private final AudioManager mAudioManager;
|
private final AudioManager mAudioManager;
|
||||||
private final Bitmap mGenericSenderIcon;
|
private final Bitmap mGenericSenderIcon;
|
||||||
private final Clock mClock;
|
private final Clock mClock;
|
||||||
|
// TODO The service context used to create and manage the notification controller is NOT
|
||||||
|
// guaranteed to live forever. As such, we may lose the data in this structure. We should
|
||||||
|
// save / restore this data upon service termination / start. We'd also want to define
|
||||||
|
// the behaviour after a restart.
|
||||||
|
/** Maps account id to the message data */
|
||||||
|
private final HashMap<Long, MessageData> mNotificationMap;
|
||||||
|
|
||||||
/** Constructor */
|
/** Constructor */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@ -74,6 +91,7 @@ public class NotificationController {
|
|||||||
mGenericSenderIcon = BitmapFactory.decodeResource(mContext.getResources(),
|
mGenericSenderIcon = BitmapFactory.decodeResource(mContext.getResources(),
|
||||||
R.drawable.ic_contact_picture);
|
R.drawable.ic_contact_picture);
|
||||||
mClock = clock;
|
mClock = clock;
|
||||||
|
mNotificationMap = new HashMap<Long, MessageData>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Singleton access */
|
/** Singleton access */
|
||||||
@ -97,11 +115,13 @@ public class NotificationController {
|
|||||||
* @param largeIcon A large icon. May be {@code null}
|
* @param largeIcon A large icon. May be {@code null}
|
||||||
* @param number A number to display using {@link Notification.Builder#setNumber(int)}. May
|
* @param number A number to display using {@link Notification.Builder#setNumber(int)}. May
|
||||||
* be {@code null}.
|
* be {@code null}.
|
||||||
|
* @param enableAudio If {@code false}, do not play any sound. Otherwise, play sound according
|
||||||
|
* to the settings for the given account.
|
||||||
* @return A {@link Notification} that can be sent to the notification service.
|
* @return A {@link Notification} that can be sent to the notification service.
|
||||||
*/
|
*/
|
||||||
private Notification createAccountNotification(Account account, String ticker,
|
private Notification createAccountNotification(Account account, String ticker,
|
||||||
CharSequence title, String contentText, Intent intent, Bitmap largeIcon,
|
CharSequence title, String contentText, Intent intent, Bitmap largeIcon,
|
||||||
Integer number) {
|
Integer number, boolean enableAudio) {
|
||||||
// Pending Intent
|
// Pending Intent
|
||||||
PendingIntent pending = null;
|
PendingIntent pending = null;
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
@ -119,7 +139,10 @@ public class NotificationController {
|
|||||||
.setSmallIcon(R.drawable.stat_notify_email_generic)
|
.setSmallIcon(R.drawable.stat_notify_email_generic)
|
||||||
.setWhen(mClock.getTime())
|
.setWhen(mClock.getTime())
|
||||||
.setTicker(ticker);
|
.setTicker(ticker);
|
||||||
setupSoundAndVibration(builder, account);
|
|
||||||
|
if (enableAudio) {
|
||||||
|
setupSoundAndVibration(builder, account);
|
||||||
|
}
|
||||||
|
|
||||||
Notification notification = builder.getNotification();
|
Notification notification = builder.getNotification();
|
||||||
return notification;
|
return notification;
|
||||||
@ -137,8 +160,8 @@ public class NotificationController {
|
|||||||
*/
|
*/
|
||||||
private void showAccountNotification(Account account, String ticker, String title,
|
private void showAccountNotification(Account account, String ticker, String title,
|
||||||
String contentText, Intent intent, int notificationId) {
|
String contentText, Intent intent, int notificationId) {
|
||||||
Notification notification = //nb.getNotification();
|
Notification notification = createAccountNotification(account, ticker, title, contentText,
|
||||||
createAccountNotification(account, ticker, title, contentText, intent, null, null);
|
intent, null, null, true);
|
||||||
mNotificationManager.notify(notificationId, notification);
|
mNotificationManager.notify(notificationId, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,16 +188,35 @@ public class NotificationController {
|
|||||||
* @param accountId The ID of the account to cancel for. If {@code -1}, "new message"
|
* @param accountId The ID of the account to cancel for. If {@code -1}, "new message"
|
||||||
* notifications for all accounts will be canceled.
|
* notifications for all accounts will be canceled.
|
||||||
*/
|
*/
|
||||||
public void cancelNewMessageNotification(long accountId) {
|
public void cancelNewMessageNotification(final long accountId) {
|
||||||
if (accountId == -1) {
|
if (accountId == -1) {
|
||||||
new Utility.ForEachAccount(mContext) {
|
for (long id : mNotificationMap.keySet()) {
|
||||||
@Override
|
cancelNewMessageNotification(id);
|
||||||
protected void performAction(long accountId) {
|
}
|
||||||
cancelNewMessageNotification(accountId);
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
} else {
|
} else {
|
||||||
|
MessageData data = mNotificationMap.remove(accountId);
|
||||||
|
if (data == null) {
|
||||||
|
// Not in map; nothing to do here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ensure we don't accidentally double-cancel a notification
|
||||||
|
final ContentObserver myObserver = data.mObserver;
|
||||||
|
data.mObserver = null;
|
||||||
mNotificationManager.cancel(getNewMessageNotificationId(accountId));
|
mNotificationManager.cancel(getNewMessageNotificationId(accountId));
|
||||||
|
|
||||||
|
// now do the database work
|
||||||
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
if (myObserver != null) {
|
||||||
|
resolver.unregisterContentObserver(myObserver);
|
||||||
|
}
|
||||||
|
Uri uri = Account.RESET_NEW_MESSAGE_COUNT_URI;
|
||||||
|
uri = ContentUris.withAppendedId(uri, accountId);
|
||||||
|
resolver.update(uri, null, null, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,17 +224,58 @@ public class NotificationController {
|
|||||||
* Show (or update) a "new message" notification for the given account.
|
* Show (or update) a "new message" notification for the given account.
|
||||||
*
|
*
|
||||||
* @param accountId The ID of the account to display a notification for.
|
* @param accountId The ID of the account to display a notification for.
|
||||||
* @param unseenMessageCount The number of messages in the account that are unseen.
|
* @param addedMessages A list of new message IDs added to the given account.
|
||||||
*/
|
*/
|
||||||
public void showNewMessageNotification(final long accountId, final int unseenMessageCount,
|
public void showNewMessageNotification(final long accountId,
|
||||||
final int justFetchedCount) {
|
final ArrayList<Long> addedMessages) {
|
||||||
|
if (addedMessages == null || addedMessages.size() == 0) {
|
||||||
|
// No messages added; nothing to do here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MessageData data = mNotificationMap.get(accountId);
|
||||||
|
if (data == null) {
|
||||||
|
data = new MessageData();
|
||||||
|
mNotificationMap.put(accountId, data);
|
||||||
|
}
|
||||||
|
final HashSet<Long> idSet = data.mMessageList;
|
||||||
|
synchronized (idSet) {
|
||||||
|
idSet.addAll(addedMessages);
|
||||||
|
}
|
||||||
|
// Pick a message to observe
|
||||||
|
final long messageId = idSet.iterator().next();
|
||||||
|
final ContentObserver myObserver;
|
||||||
|
if (data.mObserver == null) {
|
||||||
|
myObserver = new MessageContentObserver(Utility.getMainThreadHandler(), mContext,
|
||||||
|
accountId, messageId);
|
||||||
|
data.mObserver = myObserver;
|
||||||
|
} else {
|
||||||
|
myObserver = data.mObserver;
|
||||||
|
}
|
||||||
|
|
||||||
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Notification n = createNewMessageNotification(accountId, unseenMessageCount);
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
// Atomically update the unseen count
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT);
|
||||||
|
cv.put(EmailContent.ADD_COLUMN_NAME, addedMessages.size());
|
||||||
|
Uri uri = ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, accountId);
|
||||||
|
resolver.update(uri, cv, null, null);
|
||||||
|
// Get the unseen count
|
||||||
|
uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
|
||||||
|
int unseenMessageCount = Utility.getFirstRowInt(mContext, uri,
|
||||||
|
new String[] { AccountColumns.NEW_MESSAGE_COUNT }, null /*selection*/,
|
||||||
|
null /*selectionArgs*/, null /*sortOrder*/, 0 /*column*/, 0 /*default*/);
|
||||||
|
// Create the notification
|
||||||
|
Notification n = createNewMessageNotification(accountId, unseenMessageCount, true);
|
||||||
if (n == null) {
|
if (n == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Register a content observer with one of the messages
|
||||||
|
uri = ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId);
|
||||||
|
resolver.registerContentObserver(uri, false, myObserver);
|
||||||
|
// Make the notification visible
|
||||||
mNotificationManager.notify(getNewMessageNotificationId(accountId), n);
|
mNotificationManager.notify(getNewMessageNotificationId(accountId), n);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -222,7 +305,8 @@ public class NotificationController {
|
|||||||
* NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS)
|
* NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS)
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Notification createNewMessageNotification(long accountId, int unseenMessageCount) {
|
Notification createNewMessageNotification(long accountId, int unseenMessageCount,
|
||||||
|
boolean enableAudio) {
|
||||||
final Account account = Account.restoreAccountWithId(mContext, accountId);
|
final Account account = Account.restoreAccountWithId(mContext, accountId);
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -244,8 +328,8 @@ public class NotificationController {
|
|||||||
final Bitmap largeIcon = senderPhoto != null ? senderPhoto : mGenericSenderIcon;
|
final Bitmap largeIcon = senderPhoto != null ? senderPhoto : mGenericSenderIcon;
|
||||||
final Integer number = unseenMessageCount > 1 ? unseenMessageCount : null;
|
final Integer number = unseenMessageCount > 1 ? unseenMessageCount : null;
|
||||||
|
|
||||||
Notification notification =
|
Notification notification = createAccountNotification(account, null, title, subject,
|
||||||
createAccountNotification(account, null, title, subject, intent, largeIcon, number);
|
intent, largeIcon, number, enableAudio);
|
||||||
return notification;
|
return notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,4 +500,119 @@ public class NotificationController {
|
|||||||
public void cancelSecurityNeededNotification() {
|
public void cancelSecurityNeededNotification() {
|
||||||
cancelNotification(NOTIFICATION_ID_SECURITY_NEEDED);
|
cancelNotification(NOTIFICATION_ID_SECURITY_NEEDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observer invoked whenever a message we're notifying the user about changes.
|
||||||
|
*/
|
||||||
|
private static class MessageContentObserver extends ContentObserver {
|
||||||
|
/** The account this observer is attached to */
|
||||||
|
private final long mAccountId;
|
||||||
|
/** A singular message ID to notify on */
|
||||||
|
private final long mMessageId;
|
||||||
|
/** The context */
|
||||||
|
private final Context mContext;
|
||||||
|
/** The handler we will be invoked on */
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
|
MessageContentObserver(Handler handler, Context context, long accountId,
|
||||||
|
long messageId) {
|
||||||
|
super (handler);
|
||||||
|
mHandler = handler;
|
||||||
|
mContext = context;
|
||||||
|
mAccountId = accountId;
|
||||||
|
mMessageId = messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange) {
|
||||||
|
super.onChange(selfChange);
|
||||||
|
final MessageData data = sInstance.mNotificationMap.get(mAccountId);
|
||||||
|
// If this account had been removed from the set of notifications or if the observer
|
||||||
|
// has been updated, make sure we don't get called again
|
||||||
|
if (data == null || data.mObserver != this) {
|
||||||
|
mContext.getContentResolver().unregisterContentObserver(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we're only handling one change at a time
|
||||||
|
EmailAsyncTask.runAsyncSerial(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleChange(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs any database operations to handle an observed change.
|
||||||
|
*
|
||||||
|
* NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS)
|
||||||
|
* @param data Message data for the observed account
|
||||||
|
*/
|
||||||
|
private void handleChange(MessageData data) {
|
||||||
|
Message message = Message.restoreMessageWithId(mContext, mMessageId);
|
||||||
|
if (message != null && !message.mFlagRead) {
|
||||||
|
// do nothing; wait until this message is modified
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// message removed or read; get another one in the list and update the notification
|
||||||
|
// Remove ourselves from the set of notifiers
|
||||||
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
resolver.unregisterContentObserver(this);
|
||||||
|
synchronized (data.mMessageList) {
|
||||||
|
data.mMessageList.remove(mMessageId);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (;;) {
|
||||||
|
long nextMessageId = data.mMessageList.iterator().next();
|
||||||
|
Message nextMessage = Message.restoreMessageWithId(mContext, nextMessageId);
|
||||||
|
if ((nextMessage == null) || (nextMessage.mFlagRead)) {
|
||||||
|
synchronized (data.mMessageList) {
|
||||||
|
data.mMessageList.remove(nextMessageId);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data.mObserver = new MessageContentObserver(mHandler, mContext, mAccountId,
|
||||||
|
nextMessageId);
|
||||||
|
Uri uri = ContentUris.withAppendedId(
|
||||||
|
EmailContent.Message.CONTENT_URI, nextMessageId);
|
||||||
|
resolver.registerContentObserver(uri, false, data.mObserver);
|
||||||
|
|
||||||
|
// Update the new message count
|
||||||
|
int unseenMessageCount = data.mMessageList.size();
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
|
||||||
|
cv.put(EmailContent.SET_COLUMN_NAME, unseenMessageCount);
|
||||||
|
uri = ContentUris.withAppendedId(
|
||||||
|
Account.RESET_NEW_MESSAGE_COUNT_URI, mAccountId);
|
||||||
|
resolver.update(uri, cv, null, null);
|
||||||
|
|
||||||
|
// Re-display the notification w/o audio
|
||||||
|
Notification n = sInstance.createNewMessageNotification(mAccountId,
|
||||||
|
unseenMessageCount, false);
|
||||||
|
sInstance.mNotificationManager.notify(
|
||||||
|
sInstance.getNewMessageNotificationId(mAccountId), n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// this is not an error; it means the list is empty, so, hide the notification
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// make sure we're on the UI thread to cancel the notification
|
||||||
|
sInstance.cancelNewMessageNotification(mAccountId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the message(s) we're notifying the user about.
|
||||||
|
*/
|
||||||
|
private static class MessageData {
|
||||||
|
final HashSet<Long> mMessageList = new HashSet<Long>();
|
||||||
|
ContentObserver mObserver;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,7 +385,8 @@ public class RefreshManager {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void updateMailboxCallback(MessagingException exception, long accountId,
|
public void updateMailboxCallback(MessagingException exception, long accountId,
|
||||||
long mailboxId, int progress, int dontUseNumNewMessages) {
|
long mailboxId, int progress, int dontUseNumNewMessages,
|
||||||
|
ArrayList<Long> addedMessages) {
|
||||||
if (LOG_ENABLED) {
|
if (LOG_ENABLED) {
|
||||||
Log.d(Logging.LOG_TAG, "updateMailboxCallback " + accountId + ", "
|
Log.d(Logging.LOG_TAG, "updateMailboxCallback " + accountId + ", "
|
||||||
+ mailboxId + ", " + progress + ", " + exceptionToString(exception));
|
+ mailboxId + ", " + progress + ", " + exceptionToString(exception));
|
||||||
|
@ -28,6 +28,8 @@ import com.android.emailcommon.provider.EmailContent;
|
|||||||
import com.android.emailcommon.provider.EmailContent.Account;
|
import com.android.emailcommon.provider.EmailContent.Account;
|
||||||
import com.android.emailcommon.provider.EmailContent.Mailbox;
|
import com.android.emailcommon.provider.EmailContent.Mailbox;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
@ -286,7 +288,8 @@ public class AccountFolderList extends Activity implements AccountFolderListFrag
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMailboxCallback(MessagingException result, long accountKey,
|
public void updateMailboxCallback(MessagingException result, long accountKey,
|
||||||
long mailboxKey, int progress, int numNewMessages) {
|
long mailboxKey, int progress, int numNewMessages,
|
||||||
|
ArrayList<Long> addedMessages) {
|
||||||
updateProgress(result, progress);
|
updateProgress(result, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ import com.android.emailcommon.provider.EmailContent.Message;
|
|||||||
import com.android.emailcommon.provider.EmailContent.MessageColumns;
|
import com.android.emailcommon.provider.EmailContent.MessageColumns;
|
||||||
import com.android.emailcommon.utility.Utility;
|
import com.android.emailcommon.utility.Utility;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ListFragment;
|
import android.app.ListFragment;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -448,7 +450,8 @@ public class AccountFolderListFragment extends ListFragment
|
|||||||
private class ControllerResults extends Controller.Result {
|
private class ControllerResults extends Controller.Result {
|
||||||
@Override
|
@Override
|
||||||
public void updateMailboxCallback(MessagingException result, long accountKey,
|
public void updateMailboxCallback(MessagingException result, long accountKey,
|
||||||
long mailboxKey, int progress, int numNewMessages) {
|
long mailboxKey, int progress, int numNewMessages,
|
||||||
|
ArrayList<Long> addedMessages) {
|
||||||
if (progress == 100) {
|
if (progress == 100) {
|
||||||
updateAccounts();
|
updateAccounts();
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import com.android.email.Controller;
|
|||||||
import com.android.email.ControllerResultUiThreadWrapper;
|
import com.android.email.ControllerResultUiThreadWrapper;
|
||||||
import com.android.email.Email;
|
import com.android.email.Email;
|
||||||
import com.android.email.MessagingExceptionStrings;
|
import com.android.email.MessagingExceptionStrings;
|
||||||
|
import com.android.email.NotificationController;
|
||||||
import com.android.email.R;
|
import com.android.email.R;
|
||||||
import com.android.emailcommon.Logging;
|
import com.android.emailcommon.Logging;
|
||||||
import com.android.emailcommon.mail.MessagingException;
|
import com.android.emailcommon.mail.MessagingException;
|
||||||
@ -51,6 +52,7 @@ import android.view.View;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main Email activity, which is used on both the tablet and the phone.
|
* The main Email activity, which is used on both the tablet and the phone.
|
||||||
@ -480,7 +482,7 @@ public class EmailActivity extends Activity implements View.OnClickListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMailboxCallback(MessagingException result, long accountId, long mailboxId,
|
public void updateMailboxCallback(MessagingException result, long accountId, long mailboxId,
|
||||||
int progress, int numNewMessages) {
|
int progress, int numNewMessages, ArrayList<Long> addedMessages) {
|
||||||
handleError(result, accountId, progress);
|
handleError(result, accountId, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ import com.android.emailcommon.provider.EmailContent.Account;
|
|||||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||||
import com.android.emailcommon.utility.Utility;
|
import com.android.emailcommon.utility.Utility;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import android.app.ActionBar;
|
import android.app.ActionBar;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
@ -288,7 +290,7 @@ public class MailboxList extends Activity implements MailboxListFragment.Callbac
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMailboxCallback(MessagingException result, long accountKey,
|
public void updateMailboxCallback(MessagingException result, long accountKey,
|
||||||
long mailboxKey, int progress, int numNewMessages) {
|
long mailboxKey, int progress, int numNewMessages, ArrayList<Long> addedMessages) {
|
||||||
if (accountKey == mAccountId) {
|
if (accountKey == mAccountId) {
|
||||||
updateBanner(result, progress);
|
updateBanner(result, progress);
|
||||||
updateProgress(result, progress);
|
updateProgress(result, progress);
|
||||||
|
@ -242,14 +242,6 @@ public abstract class Store {
|
|||||||
return com.android.email.activity.setup.AccountSetupIncoming.class;
|
return com.android.email.activity.setup.AccountSetupIncoming.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get class of sync'er for this Store class
|
|
||||||
* @return Message Sync controller, or null to use default
|
|
||||||
*/
|
|
||||||
public StoreSynchronizer getMessageSynchronizer() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some stores cannot download a message based only on the uid, and need the message structure
|
* Some stores cannot download a message based only on the uid, and need the message structure
|
||||||
* to be preloaded and provided to them. This method allows a remote store to signal this
|
* to be preloaded and provided to them. This method allows a remote store to signal this
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009 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.mail;
|
|
||||||
|
|
||||||
import com.android.email.MessagingListener;
|
|
||||||
import com.android.emailcommon.mail.MessagingException;
|
|
||||||
import com.android.emailcommon.provider.EmailContent;
|
|
||||||
import com.android.email.GroupMessagingListener;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface allows a store to define a completely different synchronizer algorithm,
|
|
||||||
* as necessary.
|
|
||||||
*/
|
|
||||||
public interface StoreSynchronizer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object of this class is returned by SynchronizeMessagesSynchronous to report
|
|
||||||
* the results of the sync run.
|
|
||||||
*/
|
|
||||||
public static class SyncResults {
|
|
||||||
/**
|
|
||||||
* The total # of messages in the folder
|
|
||||||
*/
|
|
||||||
public int mTotalMessages;
|
|
||||||
/**
|
|
||||||
* The # of new messages in the folder
|
|
||||||
*/
|
|
||||||
public int mNewMessages;
|
|
||||||
|
|
||||||
public SyncResults(int totalMessages, int newMessages) {
|
|
||||||
mTotalMessages = totalMessages;
|
|
||||||
mNewMessages = newMessages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The job of this method is to synchronize messages between a remote folder and the
|
|
||||||
* corresponding local folder.
|
|
||||||
*
|
|
||||||
* The following callbacks should be called during this operation:
|
|
||||||
* {@link MessagingListener#synchronizeMailboxNewMessage(Account, String, Message)}
|
|
||||||
* {@link MessagingListener#synchronizeMailboxRemovedMessage(Account, String, Message)}
|
|
||||||
*
|
|
||||||
* Callbacks (through listeners) *must* be synchronized on the listeners object, e.g.
|
|
||||||
* synchronized (listeners) {
|
|
||||||
* for(MessagingListener listener : listeners) {
|
|
||||||
* listener.synchronizeMailboxNewMessage(account, folder, message);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @param account The account to synchronize
|
|
||||||
* @param folder The folder to synchronize
|
|
||||||
* @param listeners callbacks to make during sync operation
|
|
||||||
* @param context if needed for making system calls
|
|
||||||
* @return an object describing the sync results
|
|
||||||
*/
|
|
||||||
public SyncResults SynchronizeMessagesSynchronous(
|
|
||||||
EmailContent.Account account, EmailContent.Mailbox folder,
|
|
||||||
GroupMessagingListener listeners, Context context) throws MessagingException;
|
|
||||||
|
|
||||||
}
|
|
@ -18,7 +18,6 @@ package com.android.email.mail.store;
|
|||||||
|
|
||||||
import com.android.email.ExchangeUtils;
|
import com.android.email.ExchangeUtils;
|
||||||
import com.android.email.mail.Store;
|
import com.android.email.mail.Store;
|
||||||
import com.android.email.mail.StoreSynchronizer;
|
|
||||||
import com.android.emailcommon.mail.Folder;
|
import com.android.emailcommon.mail.Folder;
|
||||||
import com.android.emailcommon.mail.MessagingException;
|
import com.android.emailcommon.mail.MessagingException;
|
||||||
import com.android.emailcommon.provider.EmailContent.Account;
|
import com.android.emailcommon.provider.EmailContent.Account;
|
||||||
@ -83,18 +82,6 @@ public class ExchangeStore extends Store {
|
|||||||
return com.android.email.activity.setup.AccountSetupExchange.class;
|
return com.android.email.activity.setup.AccountSetupExchange.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get class of sync'er for this Store class. Because exchange Sync rules are so different
|
|
||||||
* than IMAP or POP3, it's likely that an Exchange implementation will need its own sync
|
|
||||||
* controller. If so, this function must return a non-null value.
|
|
||||||
*
|
|
||||||
* @return Message Sync controller, or null to use default
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public StoreSynchronizer getMessageSynchronizer() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inform MessagingController that this store requires message structures to be prefetched
|
* Inform MessagingController that this store requires message structures to be prefetched
|
||||||
* before it can fetch message bodies (this is due to EAS protocol restrictions.)
|
* before it can fetch message bodies (this is due to EAS protocol restrictions.)
|
||||||
|
@ -1664,8 +1664,16 @@ public class EmailProvider extends ContentProvider {
|
|||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
cache.lock(id);
|
cache.lock(id);
|
||||||
}
|
}
|
||||||
|
ContentValues newMessageCount = CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT;
|
||||||
|
if (values != null) {
|
||||||
|
Long set = values.getAsLong(EmailContent.SET_COLUMN_NAME);
|
||||||
|
if (set != null) {
|
||||||
|
newMessageCount = new ContentValues();
|
||||||
|
newMessageCount.put(Account.NEW_MESSAGE_COUNT, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
result = db.update(tableName, CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT,
|
result = db.update(tableName, newMessageCount,
|
||||||
whereWithId(id, selection), selectionArgs);
|
whereWithId(id, selection), selectionArgs);
|
||||||
} finally {
|
} finally {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
|
@ -25,16 +25,16 @@ import com.android.email.VendorPolicyLoader;
|
|||||||
import com.android.emailcommon.Configuration;
|
import com.android.emailcommon.Configuration;
|
||||||
import com.android.emailcommon.Device;
|
import com.android.emailcommon.Device;
|
||||||
import com.android.emailcommon.service.IAccountService;
|
import com.android.emailcommon.service.IAccountService;
|
||||||
import com.android.emailcommon.utility.Utility;
|
import com.android.emailcommon.utility.EmailAsyncTask;
|
||||||
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class AccountService extends Service {
|
public class AccountService extends Service {
|
||||||
|
|
||||||
@ -44,37 +44,38 @@ public class AccountService extends Service {
|
|||||||
private final IAccountService.Stub mBinder = new IAccountService.Stub() {
|
private final IAccountService.Stub mBinder = new IAccountService.Stub() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyLoginFailed(long accountId) throws RemoteException {
|
public void notifyLoginFailed(long accountId) {
|
||||||
NotificationController.getInstance(mContext).showLoginFailedNotification(accountId);
|
NotificationController.getInstance(mContext).showLoginFailedNotification(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyLoginSucceeded(long accountId) throws RemoteException {
|
public void notifyLoginSucceeded(long accountId) {
|
||||||
NotificationController.getInstance(mContext).cancelLoginFailedNotification(accountId);
|
NotificationController.getInstance(mContext).cancelLoginFailedNotification(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyNewMessages(long accountId) throws RemoteException {
|
@SuppressWarnings("unchecked")
|
||||||
MailService.actionNotifyNewMessages(mContext, accountId);
|
public void notifyNewMessages(long accountId, List messageIdList) {
|
||||||
|
MailService.actionNotifyNewMessages(mContext, accountId, messageIdList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreAccountsIfNeeded() throws RemoteException {
|
public void restoreAccountsIfNeeded() {
|
||||||
AccountBackupRestore.restoreAccountsIfNeeded(mContext);
|
AccountBackupRestore.restoreAccountsIfNeeded(mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accountDeleted() throws RemoteException {
|
public void accountDeleted() {
|
||||||
MailService.accountDeleted(mContext);
|
MailService.accountDeleted(mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAccountColor(long accountId) throws RemoteException {
|
public int getAccountColor(long accountId) {
|
||||||
return ResourceHelper.getInstance(mContext).getAccountColor(accountId);
|
return ResourceHelper.getInstance(mContext).getAccountColor(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bundle getConfigurationData(String accountType) throws RemoteException {
|
public Bundle getConfigurationData(String accountType) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putBoolean(Configuration.EXCHANGE_CONFIGURATION_USE_ALTERNATE_STRINGS,
|
bundle.putBoolean(Configuration.EXCHANGE_CONFIGURATION_USE_ALTERNATE_STRINGS,
|
||||||
VendorPolicyLoader.getInstance(mContext).useAlternateExchangeStrings());
|
VendorPolicyLoader.getInstance(mContext).useAlternateExchangeStrings());
|
||||||
@ -82,9 +83,9 @@ public class AccountService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDeviceId() throws RemoteException {
|
public String getDeviceId() {
|
||||||
try {
|
try {
|
||||||
Utility.runAsync(new Runnable() {
|
EmailAsyncTask.runAsyncSerial(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Make sure the service is properly running (re: lifecycle)
|
// Make sure the service is properly running (re: lifecycle)
|
||||||
|
@ -27,11 +27,11 @@ import com.android.emailcommon.AccountManagerTypes;
|
|||||||
import com.android.emailcommon.mail.MessagingException;
|
import com.android.emailcommon.mail.MessagingException;
|
||||||
import com.android.emailcommon.provider.EmailContent;
|
import com.android.emailcommon.provider.EmailContent;
|
||||||
import com.android.emailcommon.provider.EmailContent.Account;
|
import com.android.emailcommon.provider.EmailContent.Account;
|
||||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
|
||||||
import com.android.emailcommon.provider.EmailContent.HostAuth;
|
import com.android.emailcommon.provider.EmailContent.HostAuth;
|
||||||
import com.android.emailcommon.provider.EmailContent.Mailbox;
|
import com.android.emailcommon.provider.EmailContent.Mailbox;
|
||||||
import com.android.emailcommon.utility.AccountReconciler;
|
import com.android.emailcommon.utility.AccountReconciler;
|
||||||
import com.android.emailcommon.utility.Utility;
|
import com.android.emailcommon.utility.EmailAsyncTask;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.accounts.AccountManagerCallback;
|
import android.accounts.AccountManagerCallback;
|
||||||
@ -47,7 +47,6 @@ import android.database.Cursor;
|
|||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@ -56,6 +55,7 @@ import android.util.Log;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background service for refreshing non-push email accounts.
|
* Background service for refreshing non-push email accounts.
|
||||||
@ -82,24 +82,25 @@ public class MailService extends Service {
|
|||||||
private static final String EXTRA_ACCOUNT = "com.android.email.intent.extra.ACCOUNT";
|
private static final String EXTRA_ACCOUNT = "com.android.email.intent.extra.ACCOUNT";
|
||||||
private static final String EXTRA_ACCOUNT_INFO = "com.android.email.intent.extra.ACCOUNT_INFO";
|
private static final String EXTRA_ACCOUNT_INFO = "com.android.email.intent.extra.ACCOUNT_INFO";
|
||||||
private static final String EXTRA_DEBUG_WATCHDOG = "com.android.email.intent.extra.WATCHDOG";
|
private static final String EXTRA_DEBUG_WATCHDOG = "com.android.email.intent.extra.WATCHDOG";
|
||||||
|
private static final String EXTRA_MESSAGE_ID_COUNT =
|
||||||
|
"com.android.email.intent.extra.MESSAGE_ID_COUNT";
|
||||||
|
private static final String EXTRA_MESSAGE_ID_PREFIX =
|
||||||
|
"com.android.email.intent.extra.MESSAGE_ID_";
|
||||||
|
|
||||||
private static final int WATCHDOG_DELAY = 10 * 60 * 1000; // 10 minutes
|
/** Time between watchdog checks; in milliseconds */
|
||||||
|
private static final long WATCHDOG_DELAY = 10 * 60 * 1000; // 10 minutes
|
||||||
|
|
||||||
// Sentinel value asking to update mSyncReports if it's currently empty
|
// Sentinel value asking to update mSyncReports if it's currently empty
|
||||||
/*package*/ static final int SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY = -1;
|
/*package*/ static final int SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY = -1;
|
||||||
// Sentinel value asking that mSyncReports be rebuilt
|
// Sentinel value asking that mSyncReports be rebuilt
|
||||||
/*package*/ static final int SYNC_REPORTS_RESET = -2;
|
/*package*/ static final int SYNC_REPORTS_RESET = -2;
|
||||||
|
|
||||||
private static final String[] NEW_MESSAGE_COUNT_PROJECTION =
|
|
||||||
new String[] {AccountColumns.NEW_MESSAGE_COUNT};
|
|
||||||
|
|
||||||
private static MailService sMailService;
|
private static MailService sMailService;
|
||||||
|
|
||||||
/*package*/ Controller mController;
|
/*package*/ Controller mController;
|
||||||
private final Controller.Result mControllerCallback = new ControllerResults();
|
private final Controller.Result mControllerCallback = new ControllerResults();
|
||||||
private ContentResolver mContentResolver;
|
private ContentResolver mContentResolver;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private Handler mHandler = new Handler();
|
|
||||||
|
|
||||||
private int mStartId;
|
private int mStartId;
|
||||||
|
|
||||||
@ -150,28 +151,7 @@ public class MailService extends Service {
|
|||||||
* @param accountId account to clear, or -1 for all accounts
|
* @param accountId account to clear, or -1 for all accounts
|
||||||
*/
|
*/
|
||||||
public static void resetNewMessageCount(final Context context, final long accountId) {
|
public static void resetNewMessageCount(final Context context, final long accountId) {
|
||||||
synchronized (mSyncReports) {
|
|
||||||
for (AccountSyncReport report : mSyncReports.values()) {
|
|
||||||
if (accountId == -1 || accountId == report.accountId) {
|
|
||||||
report.unseenMessageCount = 0;
|
|
||||||
report.lastUnseenMessageCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Clear notification
|
|
||||||
NotificationController.getInstance(context).cancelNewMessageNotification(accountId);
|
NotificationController.getInstance(context).cancelNewMessageNotification(accountId);
|
||||||
|
|
||||||
// now do the database - all accounts, or just one of them
|
|
||||||
Utility.runAsync(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Uri uri = Account.RESET_NEW_MESSAGE_COUNT_URI;
|
|
||||||
if (accountId != -1) {
|
|
||||||
uri = ContentUris.withAppendedId(uri, accountId);
|
|
||||||
}
|
|
||||||
context.getContentResolver().update(uri, null, null, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,10 +162,21 @@ public class MailService extends Service {
|
|||||||
* @param context a context
|
* @param context a context
|
||||||
* @param accountId the id of the account that is reporting new messages
|
* @param accountId the id of the account that is reporting new messages
|
||||||
*/
|
*/
|
||||||
public static void actionNotifyNewMessages(Context context, long accountId) {
|
@SuppressWarnings("unchecked")
|
||||||
|
public static void actionNotifyNewMessages(
|
||||||
|
Context context, long accountId, List messageIdList) {
|
||||||
Intent i = new Intent(ACTION_NOTIFY_MAIL);
|
Intent i = new Intent(ACTION_NOTIFY_MAIL);
|
||||||
i.setClass(context, MailService.class);
|
i.setClass(context, MailService.class);
|
||||||
i.putExtra(EXTRA_ACCOUNT, accountId);
|
i.putExtra(EXTRA_ACCOUNT, accountId);
|
||||||
|
int listSize = 0;
|
||||||
|
if (messageIdList != null) {
|
||||||
|
listSize = messageIdList.size();
|
||||||
|
for (int j = 0; j < listSize; j++) {
|
||||||
|
long messageId = (Long) messageIdList.get(j);
|
||||||
|
i.putExtra(EXTRA_MESSAGE_ID_PREFIX + j, messageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.putExtra(EXTRA_MESSAGE_ID_COUNT, listSize);
|
||||||
context.startService(i);
|
context.startService(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +194,7 @@ public class MailService extends Service {
|
|||||||
// Restore accounts, if it has not happened already
|
// Restore accounts, if it has not happened already
|
||||||
AccountBackupRestore.restoreAccountsIfNeeded(this);
|
AccountBackupRestore.restoreAccountsIfNeeded(this);
|
||||||
|
|
||||||
Utility.runAsync(new Runnable() {
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
reconcilePopImapAccountsSync(MailService.this);
|
reconcilePopImapAccountsSync(MailService.this);
|
||||||
@ -224,7 +215,7 @@ public class MailService extends Service {
|
|||||||
|
|
||||||
if (ACTION_CHECK_MAIL.equals(action)) {
|
if (ACTION_CHECK_MAIL.equals(action)) {
|
||||||
// DB access required to satisfy this intent, so offload from UI thread
|
// DB access required to satisfy this intent, so offload from UI thread
|
||||||
Utility.runAsync(new Runnable() {
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// If we have the data, restore the last-sync-times for each account
|
// If we have the data, restore the last-sync-times for each account
|
||||||
@ -280,7 +271,7 @@ public class MailService extends Service {
|
|||||||
if (Email.DEBUG) {
|
if (Email.DEBUG) {
|
||||||
Log.d(LOG_TAG, "action: delete exchange accounts");
|
Log.d(LOG_TAG, "action: delete exchange accounts");
|
||||||
}
|
}
|
||||||
Utility.runAsync(new Runnable() {
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
Cursor c = mContentResolver.query(Account.CONTENT_URI, Account.ID_PROJECTION,
|
Cursor c = mContentResolver.query(Account.CONTENT_URI, Account.ID_PROJECTION,
|
||||||
null, null, null);
|
null, null, null);
|
||||||
@ -304,7 +295,7 @@ public class MailService extends Service {
|
|||||||
if (Email.DEBUG) {
|
if (Email.DEBUG) {
|
||||||
Log.d(LOG_TAG, "action: send pending mail");
|
Log.d(LOG_TAG, "action: send pending mail");
|
||||||
}
|
}
|
||||||
Utility.runAsync(new Runnable() {
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
mController.sendPendingMessages(accountId);
|
mController.sendPendingMessages(accountId);
|
||||||
}
|
}
|
||||||
@ -315,17 +306,14 @@ public class MailService extends Service {
|
|||||||
if (Email.DEBUG) {
|
if (Email.DEBUG) {
|
||||||
Log.d(LOG_TAG, "action: reschedule");
|
Log.d(LOG_TAG, "action: reschedule");
|
||||||
}
|
}
|
||||||
final NotificationController nc = NotificationController.getInstance(this);
|
|
||||||
// DB access required to satisfy this intent, so offload from UI thread
|
// DB access required to satisfy this intent, so offload from UI thread
|
||||||
Utility.runAsync(new Runnable() {
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Clear all notifications, in case account list has changed.
|
// Clear all notifications, in case account list has changed.
|
||||||
//
|
NotificationController
|
||||||
// TODO Clear notifications for non-existing accounts. Now that we have
|
.getInstance(MailService.this)
|
||||||
// separate notifications for each account, NotificationController should be
|
.cancelNewMessageNotification(-1);
|
||||||
// able to do that.
|
|
||||||
nc.cancelNewMessageNotification(-1);
|
|
||||||
|
|
||||||
// When called externally, we refresh the sync reports table to pick up
|
// When called externally, we refresh the sync reports table to pick up
|
||||||
// any changes in the account list or account settings
|
// any changes in the account list or account settings
|
||||||
@ -337,23 +325,23 @@ public class MailService extends Service {
|
|||||||
});
|
});
|
||||||
} else if (ACTION_NOTIFY_MAIL.equals(action)) {
|
} else if (ACTION_NOTIFY_MAIL.equals(action)) {
|
||||||
// DB access required to satisfy this intent, so offload from UI thread
|
// DB access required to satisfy this intent, so offload from UI thread
|
||||||
Utility.runAsync(new Runnable() {
|
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Get the current new message count
|
int newMessageCount = intent.getIntExtra(EXTRA_MESSAGE_ID_COUNT, 0);
|
||||||
Cursor c = mContentResolver.query(
|
ArrayList<Long> messageIdList = new ArrayList<Long>();
|
||||||
ContentUris.withAppendedId(Account.CONTENT_URI, accountId),
|
for (int i = 0; i < newMessageCount; i++) {
|
||||||
NEW_MESSAGE_COUNT_PROJECTION, null, null, null);
|
final long messageId =
|
||||||
int newMessageCount = 0;
|
intent.getLongExtra(EXTRA_MESSAGE_ID_PREFIX + i, -1L);
|
||||||
try {
|
if (messageId <= 0) {
|
||||||
if (c.moveToFirst()) {
|
// What else to do here?? This should never happen ...
|
||||||
newMessageCount = c.getInt(0);
|
Log.w(LOG_TAG, "invalid message id in notification; id: " + messageId);
|
||||||
updateAccountReport(accountId, newMessageCount);
|
continue;
|
||||||
notifyNewMessages(accountId);
|
|
||||||
}
|
}
|
||||||
} finally {
|
messageIdList.add(messageId);
|
||||||
c.close();
|
|
||||||
}
|
}
|
||||||
|
updateAccountReport(accountId, newMessageCount);
|
||||||
|
notifyNewMessages(accountId, messageIdList);
|
||||||
if (Email.DEBUG) {
|
if (Email.DEBUG) {
|
||||||
Log.d(LOG_TAG, "notify accountId=" + Long.toString(accountId)
|
Log.d(LOG_TAG, "notify accountId=" + Long.toString(accountId)
|
||||||
+ " count=" + newMessageCount);
|
+ " count=" + newMessageCount);
|
||||||
@ -404,10 +392,7 @@ public class MailService extends Service {
|
|||||||
AccountSyncReport oldReport = oldSyncReports.get(newReport.accountId);
|
AccountSyncReport oldReport = oldSyncReports.get(newReport.accountId);
|
||||||
if (oldReport != null) {
|
if (oldReport != null) {
|
||||||
newReport.prevSyncTime = oldReport.prevSyncTime;
|
newReport.prevSyncTime = oldReport.prevSyncTime;
|
||||||
if (newReport.syncInterval > 0 && newReport.prevSyncTime != 0) {
|
newReport.setNextSyncTime();
|
||||||
newReport.nextSyncTime =
|
|
||||||
newReport.prevSyncTime + (newReport.syncInterval * 1000 * 60);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,38 +518,32 @@ public class MailService extends Service {
|
|||||||
* TODO: Look more closely at syncEnabled and see if we can simply coalesce it into
|
* TODO: Look more closely at syncEnabled and see if we can simply coalesce it into
|
||||||
* syncInterval (e.g. if !syncEnabled, set syncInterval to -1).
|
* syncInterval (e.g. if !syncEnabled, set syncInterval to -1).
|
||||||
*/
|
*/
|
||||||
/*package*/ static class AccountSyncReport {
|
@VisibleForTesting
|
||||||
|
static class AccountSyncReport {
|
||||||
long accountId;
|
long accountId;
|
||||||
long prevSyncTime; // 0 == unknown
|
/** The time of the last sync, or, {@code 0}, the last sync time is unknown. */
|
||||||
long nextSyncTime; // 0 == ASAP -1 == don't sync
|
long prevSyncTime;
|
||||||
|
/** The time of the next sync. If {@code 0}, sync ASAP. If {@code 1}, don't sync. */
|
||||||
/** # of "unseen" messages to show in notification */
|
long nextSyncTime;
|
||||||
int unseenMessageCount;
|
/** Minimum time between syncs; in minutes. */
|
||||||
|
int syncInterval;
|
||||||
|
/** If {@code true}, show system notifications. */
|
||||||
|
boolean notify;
|
||||||
|
/** If {@code true}, auto sync is enabled. */
|
||||||
|
boolean syncEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # of unseen, the value shown on the last notification. Used to
|
* Sets the next sync time using the previous sync time and sync interval.
|
||||||
* calculate "the number of messages that have just been fetched".
|
|
||||||
*
|
|
||||||
* TODO It's a sort of cheating. Should we use the "real" number? The only difference
|
|
||||||
* is the first notification after reboot / process restart.
|
|
||||||
*/
|
*/
|
||||||
int lastUnseenMessageCount;
|
void setNextSyncTime() {
|
||||||
|
if (syncInterval > 0 && prevSyncTime != 0) {
|
||||||
int syncInterval;
|
nextSyncTime = prevSyncTime + (syncInterval * 1000 * 60);
|
||||||
boolean notify;
|
}
|
||||||
|
|
||||||
boolean syncEnabled; // whether auto sync is enabled for this account
|
|
||||||
|
|
||||||
/** # of messages that have just been fetched */
|
|
||||||
int getJustFetchedMessageCount() {
|
|
||||||
return unseenMessageCount - lastUnseenMessageCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "id=" + accountId
|
return "id=" + accountId + " prevSync=" + prevSyncTime + " nextSync=" + nextSyncTime;
|
||||||
+ " prevSync=" + prevSyncTime + " nextSync=" + nextSyncTime + " numUnseen="
|
|
||||||
+ unseenMessageCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,8 +623,6 @@ public class MailService extends Service {
|
|||||||
report.accountId = account.mId;
|
report.accountId = account.mId;
|
||||||
report.prevSyncTime = 0;
|
report.prevSyncTime = 0;
|
||||||
report.nextSyncTime = (syncInterval > 0) ? 0 : -1; // 0 == ASAP -1 == no sync
|
report.nextSyncTime = (syncInterval > 0) ? 0 : -1; // 0 == ASAP -1 == no sync
|
||||||
report.unseenMessageCount = 0;
|
|
||||||
report.lastUnseenMessageCount = 0;
|
|
||||||
|
|
||||||
report.syncInterval = syncInterval;
|
report.syncInterval = syncInterval;
|
||||||
report.notify = (account.mFlags & Account.FLAGS_NOTIFY_NEW_MAIL) != 0;
|
report.notify = (account.mFlags & Account.FLAGS_NOTIFY_NEW_MAIL) != 0;
|
||||||
@ -685,12 +662,7 @@ public class MailService extends Service {
|
|||||||
|
|
||||||
// report found - update it (note - editing the report while in-place in the hashmap)
|
// report found - update it (note - editing the report while in-place in the hashmap)
|
||||||
report.prevSyncTime = SystemClock.elapsedRealtime();
|
report.prevSyncTime = SystemClock.elapsedRealtime();
|
||||||
if (report.syncInterval > 0) {
|
report.setNextSyncTime();
|
||||||
report.nextSyncTime = report.prevSyncTime + (report.syncInterval * 1000 * 60);
|
|
||||||
}
|
|
||||||
if (newCount != -1) {
|
|
||||||
report.unseenMessageCount = newCount;
|
|
||||||
}
|
|
||||||
if (Email.DEBUG) {
|
if (Email.DEBUG) {
|
||||||
Log.d(LOG_TAG, "update account " + report.toString());
|
Log.d(LOG_TAG, "update account " + report.toString());
|
||||||
}
|
}
|
||||||
@ -723,10 +695,7 @@ public class MailService extends Service {
|
|||||||
if (report != null) {
|
if (report != null) {
|
||||||
if (report.prevSyncTime == 0) {
|
if (report.prevSyncTime == 0) {
|
||||||
report.prevSyncTime = prevSync;
|
report.prevSyncTime = prevSync;
|
||||||
if (report.syncInterval > 0 && report.prevSyncTime != 0) {
|
report.setNextSyncTime();
|
||||||
report.nextSyncTime =
|
|
||||||
report.prevSyncTime + (report.syncInterval * 1000 * 60);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -736,7 +705,8 @@ public class MailService extends Service {
|
|||||||
class ControllerResults extends Controller.Result {
|
class ControllerResults extends Controller.Result {
|
||||||
@Override
|
@Override
|
||||||
public void updateMailboxCallback(MessagingException result, long accountId,
|
public void updateMailboxCallback(MessagingException result, long accountId,
|
||||||
long mailboxId, int progress, int numNewMessages) {
|
long mailboxId, int progress, int numNewMessages,
|
||||||
|
ArrayList<Long> addedMessages) {
|
||||||
// First, look for authentication failures and notify
|
// First, look for authentication failures and notify
|
||||||
//checkAuthenticationStatus(result, accountId);
|
//checkAuthenticationStatus(result, accountId);
|
||||||
if (result != null || progress == 100) {
|
if (result != null || progress == 100) {
|
||||||
@ -747,7 +717,7 @@ public class MailService extends Service {
|
|||||||
if (progress == 100) {
|
if (progress == 100) {
|
||||||
updateAccountReport(accountId, numNewMessages);
|
updateAccountReport(accountId, numNewMessages);
|
||||||
if (numNewMessages > 0) {
|
if (numNewMessages > 0) {
|
||||||
notifyNewMessages(accountId);
|
notifyNewMessages(accountId, addedMessages);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateAccountReport(accountId, -1);
|
updateAccountReport(accountId, -1);
|
||||||
@ -779,21 +749,16 @@ public class MailService extends Service {
|
|||||||
/**
|
/**
|
||||||
* Show "new message" notification for an account. (Notification is shown per account.)
|
* Show "new message" notification for an account. (Notification is shown per account.)
|
||||||
*/
|
*/
|
||||||
private void notifyNewMessages(final long accountId) {
|
private void notifyNewMessages(final long accountId, ArrayList<Long> addedMessages) {
|
||||||
final int unseenMessageCount;
|
|
||||||
final int justFetchedCount;
|
|
||||||
synchronized (mSyncReports) {
|
synchronized (mSyncReports) {
|
||||||
AccountSyncReport report = mSyncReports.get(accountId);
|
AccountSyncReport report = mSyncReports.get(accountId);
|
||||||
if (report == null || report.unseenMessageCount == 0 || !report.notify) {
|
if (report == null || !report.notify) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unseenMessageCount = report.unseenMessageCount;
|
|
||||||
justFetchedCount = report.getJustFetchedMessageCount();
|
|
||||||
report.lastUnseenMessageCount = report.unseenMessageCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationController.getInstance(this).showNewMessageNotification(accountId,
|
NotificationController.getInstance(this)
|
||||||
unseenMessageCount, justFetchedCount);
|
.showNewMessageNotification(accountId, addedMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -874,7 +839,8 @@ public class MailService extends Service {
|
|||||||
* @param blockExternalChanges FOR TESTING ONLY - block backups, security changes, etc.
|
* @param blockExternalChanges FOR TESTING ONLY - block backups, security changes, etc.
|
||||||
* @param resolver the content resolver for making provider updates (injected for testability)
|
* @param resolver the content resolver for making provider updates (injected for testability)
|
||||||
*/
|
*/
|
||||||
/* package */ public static void reconcileAccountsWithAccountManager(Context context,
|
@VisibleForTesting
|
||||||
|
public static void reconcileAccountsWithAccountManager(Context context,
|
||||||
List<Account> emailProviderAccounts, android.accounts.Account[] accountManagerAccounts,
|
List<Account> emailProviderAccounts, android.accounts.Account[] accountManagerAccounts,
|
||||||
boolean blockExternalChanges, ContentResolver resolver) {
|
boolean blockExternalChanges, ContentResolver resolver) {
|
||||||
boolean accountsDeleted = AccountReconciler.reconcileAccounts(context,
|
boolean accountsDeleted = AccountReconciler.reconcileAccounts(context,
|
||||||
|
@ -213,7 +213,7 @@ public class NotificationControllerTest extends AndroidTestCase {
|
|||||||
Mailbox b1 = ProviderTestUtils.setupMailbox("inbox", a1.mId, true, c, Mailbox.TYPE_INBOX);
|
Mailbox b1 = ProviderTestUtils.setupMailbox("inbox", a1.mId, true, c, Mailbox.TYPE_INBOX);
|
||||||
Message m1 = ProviderTestUtils.setupMessage("message", a1.mId, b1.mId, true, true, c);
|
Message m1 = ProviderTestUtils.setupMessage("message", a1.mId, b1.mId, true, true, c);
|
||||||
|
|
||||||
n = mTarget.createNewMessageNotification(a1.mId, 1);
|
n = mTarget.createNewMessageNotification(a1.mId, 1, true);
|
||||||
|
|
||||||
assertEquals(R.drawable.stat_notify_email_generic, n.icon);
|
assertEquals(R.drawable.stat_notify_email_generic, n.icon);
|
||||||
assertEquals(mMockClock.mTime, n.when);
|
assertEquals(mMockClock.mTime, n.when);
|
||||||
@ -223,7 +223,7 @@ public class NotificationControllerTest extends AndroidTestCase {
|
|||||||
// TODO Check content -- how?
|
// TODO Check content -- how?
|
||||||
|
|
||||||
// Case 2: 1 account, 2 unseen message
|
// Case 2: 1 account, 2 unseen message
|
||||||
n = mTarget.createNewMessageNotification(a1.mId, 2);
|
n = mTarget.createNewMessageNotification(a1.mId, 2, true);
|
||||||
|
|
||||||
assertEquals(R.drawable.stat_notify_email_generic, n.icon);
|
assertEquals(R.drawable.stat_notify_email_generic, n.icon);
|
||||||
assertEquals(mMockClock.mTime, n.when);
|
assertEquals(mMockClock.mTime, n.when);
|
||||||
@ -247,7 +247,7 @@ public class NotificationControllerTest extends AndroidTestCase {
|
|||||||
m1.save(c);
|
m1.save(c);
|
||||||
|
|
||||||
// This shouldn't crash.
|
// This shouldn't crash.
|
||||||
n = mTarget.createNewMessageNotification(a1.mId, 1);
|
n = mTarget.createNewMessageNotification(a1.mId, 1, true);
|
||||||
|
|
||||||
// Minimum test for the result
|
// Minimum test for the result
|
||||||
assertEquals(R.drawable.stat_notify_email_generic, n.icon);
|
assertEquals(R.drawable.stat_notify_email_generic, n.icon);
|
||||||
|
@ -260,7 +260,7 @@ public class RefreshManagerTest extends InstrumentationTestCase {
|
|||||||
assertTrue(mTarget.isRefreshingAnyMessageListForTest());
|
assertTrue(mTarget.isRefreshingAnyMessageListForTest());
|
||||||
|
|
||||||
// Refreshing mailbox 1...
|
// Refreshing mailbox 1...
|
||||||
mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 0, 0);
|
mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 0, 0, null);
|
||||||
|
|
||||||
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
||||||
assertFalse(mListener.mCalledOnConnectionError);
|
assertFalse(mListener.mCalledOnConnectionError);
|
||||||
@ -272,7 +272,7 @@ public class RefreshManagerTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
// Done.
|
// Done.
|
||||||
Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass());
|
Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass());
|
||||||
mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 100, 0);
|
mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 100, 0, null);
|
||||||
|
|
||||||
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
||||||
assertFalse(mListener.mCalledOnConnectionError);
|
assertFalse(mListener.mCalledOnConnectionError);
|
||||||
@ -289,7 +289,7 @@ public class RefreshManagerTest extends InstrumentationTestCase {
|
|||||||
// Refreshing mailbox 2...
|
// Refreshing mailbox 2...
|
||||||
mClock.advance();
|
mClock.advance();
|
||||||
|
|
||||||
mController.mListener.updateMailboxCallback(null, ACCOUNT_2, MAILBOX_2, 0, 0);
|
mController.mListener.updateMailboxCallback(null, ACCOUNT_2, MAILBOX_2, 0, 0, null);
|
||||||
|
|
||||||
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
||||||
assertFalse(mListener.mCalledOnConnectionError);
|
assertFalse(mListener.mCalledOnConnectionError);
|
||||||
@ -300,7 +300,7 @@ public class RefreshManagerTest extends InstrumentationTestCase {
|
|||||||
assertEquals(0, mTarget.getMessageListStatusForTest(MAILBOX_2).getLastRefreshTime());
|
assertEquals(0, mTarget.getMessageListStatusForTest(MAILBOX_2).getLastRefreshTime());
|
||||||
|
|
||||||
// Done with exception.
|
// Done with exception.
|
||||||
mController.mListener.updateMailboxCallback(EXCEPTION, ACCOUNT_2, MAILBOX_2, 0, 0);
|
mController.mListener.updateMailboxCallback(EXCEPTION, ACCOUNT_2, MAILBOX_2, 0, 0, null);
|
||||||
|
|
||||||
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
assertTrue(mListener.mCalledOnRefreshStatusChanged);
|
||||||
assertTrue(mListener.mCalledOnConnectionError);
|
assertTrue(mListener.mCalledOnConnectionError);
|
||||||
|
Loading…
Reference in New Issue
Block a user