Fixes Issue 1394

Computing the account size is pretty slow for big accounts, and making
this functionality optional should be considered.

Also, displays the number of flagged messages ("stars") in each
account in the Accounts list and each folder in the FolderList.  Needs
better presentation of the flagged message counts, but this works as a
proof-of-concept and gets the data structures built.
This commit is contained in:
Daniel Applebaum 2010-04-16 14:33:54 +00:00
parent 110a6eb1c7
commit 664c4d6a78
10 changed files with 245 additions and 45 deletions

View File

@ -5,6 +5,8 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.util.Log;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.MessagingException;
@ -422,10 +424,13 @@ public class Account
//TODO: Shouldn't this live in MessagingController?
// Why should everything be in MessagingController? This is an Account-specific operation. --danapple0
public int getUnreadMessageCount(Context context) throws MessagingException
public AccountStats getStats(Context context) throws MessagingException
{
AccountStats stats = new AccountStats();
int unreadMessageCount = 0;
int flaggedMessageCount = 0;
LocalStore localStore = getLocalStore();
stats.size = localStore.getSize();
Account.FolderMode aMode = getFolderDisplayMode();
Preferences prefs = Preferences.getPreferences(context);
for (LocalFolder folder : localStore.getPersonalNamespaces())
@ -460,10 +465,14 @@ public class Account
continue;
}
unreadMessageCount += folder.getUnreadMessageCount();
flaggedMessageCount += folder.getFlaggedMessageCount();
}
}
return unreadMessageCount;
stats.unreadMessageCount = unreadMessageCount;
stats.flaggedMessageCount = flaggedMessageCount;
Log.i(K9.LOG_TAG, "flaggedMessageCount for " + getDescription() + " = " + flaggedMessageCount);
return stats;
}
public String getUuid()

View File

@ -1078,6 +1078,8 @@ public class MessagingController implements Runnable
int newMessages = downloadMessages(account, remoteFolder, localFolder, remoteMessages, false);
int unreadMessageCount = setLocalUnreadCountToRemote(localFolder, remoteFolder, newMessages);
setLocalFlaggedCountToRemote(localFolder, remoteFolder);
for (MessagingListener l : getListeners())
{
@ -1192,6 +1194,15 @@ public class MessagingController implements Runnable
return localFolder.getMessageCount();
}
}
private void setLocalFlaggedCountToRemote(LocalFolder localFolder, Folder remoteFolder) throws MessagingException
{
int remoteFlaggedMessageCount = remoteFolder.getFlaggedMessageCount();
if (remoteFlaggedMessageCount != -1)
{
localFolder.setFlaggedMessageCount(remoteFlaggedMessageCount);
}
}
private int downloadMessages(final Account account, final Folder remoteFolder,
final LocalFolder localFolder, List<Message> inputMessages, boolean flagSyncOnly) throws MessagingException
@ -3372,18 +3383,17 @@ public class MessagingController implements Runnable
{
public void run()
{
int unreadMessageCount = 0;
try
{
unreadMessageCount = account.getUnreadMessageCount(context);
AccountStats stats = account.getStats(context);
l.accountStatusChanged(account, stats);
}
catch (MessagingException me)
{
Log.e(K9.LOG_TAG, "Count not get unread count for account " + account.getDescription(),
me);
}
l.accountStatusChanged(account, unreadMessageCount);
}
};
@ -4306,7 +4316,8 @@ public class MessagingController implements Runnable
int unreadMessageCount = 0;
try
{
unreadMessageCount = account.getUnreadMessageCount(context);
AccountStats stats = account.getStats(context);
unreadMessageCount = stats.unreadMessageCount;
}
catch (MessagingException e)
{
@ -4649,6 +4660,8 @@ public class MessagingController implements Runnable
int newCount = downloadMessages(account, remoteFolder, localFolder, messages, flagSyncOnly);
int unreadMessageCount = setLocalUnreadCountToRemote(localFolder, remoteFolder, messages.size());
setLocalFlaggedCountToRemote(localFolder, remoteFolder);
localFolder.setLastPush(System.currentTimeMillis());
localFolder.setStatus(null);

View File

@ -18,7 +18,7 @@ import java.util.List;
public class MessagingListener
{
public void accountStatusChanged(Account account, int unreadMessageCount)
public void accountStatusChanged(Account account, AccountStats stats)
{
}

View File

@ -10,6 +10,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.*;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
@ -29,7 +30,8 @@ import java.util.concurrent.ConcurrentHashMap;
public class Accounts extends K9ListActivity implements OnItemClickListener, OnClickListener
{
private static final int DIALOG_REMOVE_ACCOUNT = 1;
private ConcurrentHashMap<String, Integer> unreadMessageCounts = new ConcurrentHashMap<String, Integer>();
//private ConcurrentHashMap<String, Integer> unreadMessageCounts = new ConcurrentHashMap<String, Integer>();
private ConcurrentHashMap<String, AccountStats> accountStats = new ConcurrentHashMap<String, AccountStats>();
private ConcurrentHashMap<Account, String> pendingWork = new ConcurrentHashMap<Account, String>();
@ -97,11 +99,20 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
{
public void run()
{
AccountStats stats = accountStats.get(account.getUuid());
if (stats != null)
{
stats.size = newSize;
}
String toastText = getString(R.string.account_size_changed, account.getDescription(),
SizeFormatter.formatSize(getApplication(), oldSize), SizeFormatter.formatSize(getApplication(), newSize));;
Toast toast = Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG);
toast.show();
if (mAdapter != null)
{
mAdapter.notifyDataSetChanged();
}
}
});
}
@ -131,21 +142,33 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
ActivityListener mListener = new ActivityListener()
{
@Override
public void accountStatusChanged(Account account, int unreadMessageCount)
public void folderStatusChanged(Account account, String folderName, int unreadMessageCount)
{
Integer oldUnreadMessageCountInteger = unreadMessageCounts.get(account.getUuid());
int oldUnreadMessageCount = 0;
if (oldUnreadMessageCountInteger != null)
try
{
oldUnreadMessageCount = oldUnreadMessageCountInteger;
AccountStats stats = account.getStats(Accounts.this);
accountStatusChanged(account, stats);
}
unreadMessageCounts.put(account.getUuid(), unreadMessageCount);
mUnreadMessageCount += unreadMessageCount - oldUnreadMessageCount;
catch (Exception e)
{
Log.e(K9.LOG_TAG, "Unable to get account stats", e);
}
}
@Override
public void accountStatusChanged(Account account, AccountStats stats)
{
AccountStats oldStats = accountStats.get(account.getUuid());
int oldUnreadMessageCount = 0;
if (oldStats != null)
{
oldUnreadMessageCount = oldStats.unreadMessageCount;
}
accountStats.put(account.getUuid(), stats);
// unreadMessageCounts.put(account.getUuid(), stats.unreadMessageCount);
mUnreadMessageCount += stats.unreadMessageCount - oldUnreadMessageCount;
mHandler.dataChanged();
pendingWork.remove(account);
if (pendingWork.isEmpty())
{
mHandler.progress(Window.PROGRESS_END);
@ -161,9 +184,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
@Override
public void accountSizeChanged(Account account, long oldSize, long newSize)
{
mHandler.accountSizeChanged(account, oldSize, newSize);
}
@Override
@ -259,7 +280,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
};
private static String UNREAD_MESSAGE_COUNTS = "unreadMessageCounts";
//private static String UNREAD_MESSAGE_COUNTS = "unreadMessageCounts";
private static String ACCOUNT_STATS = "accountStats";
private static String SELECTED_CONTEXT_ACCOUNT = "selectedContextAccount";
public static final String EXTRA_STARTUP = "startup";
@ -330,10 +352,10 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
if (icicle != null)
{
Map<String, Integer> oldUnreadMessageCounts = (Map<String, Integer>)icicle.get(UNREAD_MESSAGE_COUNTS);
if (oldUnreadMessageCounts != null)
Map<String, AccountStats> oldStats = (Map<String, AccountStats>)icicle.get(ACCOUNT_STATS);
if (oldStats != null)
{
unreadMessageCounts.putAll(oldUnreadMessageCounts);
accountStats.putAll(oldStats);
}
}
}
@ -347,7 +369,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
{
outState.putString(SELECTED_CONTEXT_ACCOUNT, mSelectedContextAccount.getUuid());
}
outState.putSerializable(UNREAD_MESSAGE_COUNTS, unreadMessageCounts);
outState.putSerializable(ACCOUNT_STATS, accountStats);
}
@Override
@ -385,7 +407,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
}
pendingWork.clear();
mUnreadMessageCount = 0;
unreadMessageCounts.clear();
accountStats.clear();
for (Account account : accounts)
{
@ -743,16 +765,35 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
view.setTag(holder);
}
holder.description.setText(account.getDescription());
holder.email.setText(account.getEmail());
AccountStats stats = accountStats.get(account.getUuid());
if (stats != null)
{
holder.email.setText(SizeFormatter.formatSize(Accounts.this, stats.size)
+ (stats.flaggedMessageCount > 0 ? " / " + stats.flaggedMessageCount + "*" : ""));
}
else
{
holder.email.setText(account.getEmail());
}
String description = account.getDescription();
if (description == null || description.length() == 0)
{
description = account.getEmail();
}
holder.description.setText(description);
if (account.getEmail().equals(account.getDescription()))
{
holder.email.setVisibility(View.GONE);
}
Integer unreadMessageCount = unreadMessageCounts.get(account.getUuid());
if (unreadMessageCount != null)
Integer unreadMessageCount = null;
if (stats != null)
{
unreadMessageCount = stats.unreadMessageCount;
holder.newMessageCount.setText(Integer.toString(unreadMessageCount));
holder.newMessageCount.setVisibility(unreadMessageCount > 0 ? View.VISIBLE : View.GONE);
}

View File

@ -63,6 +63,7 @@ public class FolderList extends K9ListActivity
private FolderListHandler mHandler = new FolderListHandler();
private int mUnreadMessageCount;
private int mFlaggedMessageCount;
class FolderListHandler extends Handler
{
@ -73,7 +74,8 @@ public class FolderList extends K9ListActivity
{
public void run()
{
String dispString = mAdapter.mListener.formatHeader(FolderList.this, getString(R.string.folder_list_title, mAccount.getDescription()), mUnreadMessageCount, getTimeFormat());
String dispString = mAdapter.mListener.formatHeader(FolderList.this,
getString(R.string.folder_list_title, mAccount.getDescription()), mUnreadMessageCount, getTimeFormat());
setTitle(dispString);
@ -757,9 +759,9 @@ public class FolderList extends K9ListActivity
private ActivityListener mListener = new ActivityListener()
{
@Override
public void accountStatusChanged(Account account, int unreadMessageCount)
public void accountStatusChanged(Account account, AccountStats stats)
{
mUnreadMessageCount = unreadMessageCount;
mUnreadMessageCount = stats.unreadMessageCount;
mHandler.refreshTitle();
}
@ -1222,8 +1224,18 @@ public class FolderList extends K9ListActivity
statusText = getString(R.string.folder_push_active_symbol) + " "+ statusText;
}
if (folder.flaggedMessageCount > 0)
{
if (statusText == null)
{
statusText = "";
}
statusText += " / " + folder.flaggedMessageCount + "*";
}
if (statusText != null)
{
holder.folderStatus.setText(statusText);
holder.folderStatus.setVisibility(View.VISIBLE);
}
@ -1273,6 +1285,8 @@ public class FolderList extends K9ListActivity
public long lastChecked;
public int unreadMessageCount;
public int flaggedMessageCount;
public boolean loading;
@ -1332,7 +1346,7 @@ public class FolderList extends K9ListActivity
try
{
folder.open(Folder.OpenMode.READ_WRITE);
unreadCount = folder.getUnreadMessageCount();
// unreadCount = folder.getUnreadMessageCount();
}
catch (MessagingException me)
{
@ -1378,6 +1392,15 @@ public class FolderList extends K9ListActivity
this.status = mess;
this.unreadMessageCount = unreadCount;
try
{
this.flaggedMessageCount = folder.getFlaggedMessageCount();
}
catch (Exception e)
{
Log.e(K9.LOG_TAG, "Unable to get flaggedMessageCount", e);
}
folder.close();

View File

@ -77,6 +77,7 @@ public abstract class Folder
public abstract int getMessageCount() throws MessagingException;
public abstract int getUnreadMessageCount() throws MessagingException;
public abstract int getFlaggedMessageCount() throws MessagingException;
public abstract Message getMessage(String uid) throws MessagingException;

View File

@ -862,6 +862,34 @@ public class ImapStore extends Store
throw ioExceptionHandler(mConnection, ioe);
}
}
@Override
public int getFlaggedMessageCount() throws MessagingException
{
checkOpen();
try
{
int count = 0;
int start = mMessageCount - 299;
if (start < 1)
{
start = 1;
}
List<ImapResponse> responses = executeSimpleCommand(String.format("SEARCH %d:* FLAGGED NOT DELETED", start));
for (ImapResponse response : responses)
{
if (response.get(0).equals("SEARCH"))
{
count += response.size() - 1;
}
}
return count;
}
catch (IOException ioe)
{
throw ioExceptionHandler(mConnection, ioe);
}
}
@Override
public void delete(boolean recurse) throws MessagingException

View File

@ -34,7 +34,7 @@ import java.util.regex.Matcher;
*/
public class LocalStore extends Store implements Serializable
{
private static final int DB_VERSION = 33;
private static final int DB_VERSION = 34;
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN, Flag.FLAGGED };
private String mPath;
@ -201,6 +201,20 @@ public class LocalStore extends Store implements Serializable
}
}
if (mDb.getVersion() < 34)
{
try
{
mDb.execSQL("ALTER TABLE folders ADD flagged_count INTEGER default 0");
}
catch (SQLiteException e)
{
if (! e.toString().startsWith("duplicate column name: flagged_count"))
{
throw e;
}
}
}
}
@ -339,11 +353,11 @@ public class LocalStore extends Store implements Serializable
try
{
cursor = mDb.rawQuery("SELECT id, name, unread_count, visible_limit, last_updated, status, push_state, last_pushed FROM folders", null);
cursor = mDb.rawQuery("SELECT id, name, unread_count, visible_limit, last_updated, status, push_state, last_pushed, flagged_count FROM folders", null);
while (cursor.moveToNext())
{
LocalFolder folder = new LocalFolder(cursor.getString(1));
folder.open(cursor.getInt(0), cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getLong(4), cursor.getString(5), cursor.getString(6), cursor.getLong(7));
folder.open(cursor.getInt(0), cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getLong(4), cursor.getString(5), cursor.getString(6), cursor.getLong(7), cursor.getInt(8));
folders.add(folder);
}
@ -743,6 +757,7 @@ public class LocalStore extends Store implements Serializable
private String mName = null;
private long mFolderId = -1;
private int mUnreadMessageCount = -1;
private int mFlaggedMessageCount = -1;
private int mVisibleLimit = -1;
private FolderClass displayClass = FolderClass.NO_CLASS;
private FolderClass syncClass = FolderClass.INHERITED;
@ -790,7 +805,7 @@ public class LocalStore extends Store implements Serializable
try
{
String baseQuery =
"SELECT id, name,unread_count, visible_limit, last_updated, status, push_state, last_pushed FROM folders ";
"SELECT id, name,unread_count, visible_limit, last_updated, status, push_state, last_pushed, flagged_count FROM folders ";
if (mName != null)
{
cursor = mDb.rawQuery(baseQuery + "where folders.name = ?", new String[] { mName });
@ -807,7 +822,7 @@ public class LocalStore extends Store implements Serializable
int folderId = cursor.getInt(0);
if (folderId > 0)
{
open(folderId, cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getLong(4), cursor.getString(5), cursor.getString(6), cursor.getLong(7));
open(folderId, cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getLong(4), cursor.getString(5), cursor.getString(6), cursor.getLong(7), cursor.getInt(8));
}
}
else
@ -826,13 +841,14 @@ public class LocalStore extends Store implements Serializable
}
}
private void open(int id, String name, int unreadCount, int visibleLimit, long lastChecked, String status, String pushState, long lastPushed) throws MessagingException
private void open(int id, String name, int unreadCount, int visibleLimit, long lastChecked, String status, String pushState, long lastPushed, int flaggedCount) throws MessagingException
{
mFolderId = id;
mName = name;
mUnreadMessageCount = unreadCount;
mVisibleLimit = visibleLimit;
mPushState = pushState;
mFlaggedMessageCount = flaggedCount;
super.setStatus(status);
// Only want to set the local variable stored in the super class. This class
// does a DB update on setLastChecked
@ -954,7 +970,13 @@ public class LocalStore extends Store implements Serializable
open(OpenMode.READ_WRITE);
return mUnreadMessageCount;
}
@Override
public int getFlaggedMessageCount() throws MessagingException
{
open(OpenMode.READ_WRITE);
return mFlaggedMessageCount;
}
public void setUnreadMessageCount(int unreadMessageCount) throws MessagingException
{
@ -963,6 +985,14 @@ public class LocalStore extends Store implements Serializable
mDb.execSQL("UPDATE folders SET unread_count = ? WHERE id = ?",
new Object[] { mUnreadMessageCount, mFolderId });
}
public void setFlaggedMessageCount(int flaggedMessageCount) throws MessagingException
{
open(OpenMode.READ_WRITE);
mFlaggedMessageCount = Math.max(0, flaggedMessageCount);
mDb.execSQL("UPDATE folders SET flagged_count = ? WHERE id = ?",
new Object[] { mFlaggedMessageCount, mFolderId });
}
@Override
public void setLastChecked(long lastChecked) throws MessagingException
@ -1524,6 +1554,12 @@ public class LocalStore extends Store implements Serializable
setUnreadMessageCount(getUnreadMessageCount() - 1);
lDestFolder.setUnreadMessageCount(lDestFolder.getUnreadMessageCount() + 1);
}
if (message.isSet(Flag.FLAGGED))
{
setFlaggedMessageCount(getFlaggedMessageCount() - 1);
lDestFolder.setFlaggedMessageCount(lDestFolder.getFlaggedMessageCount() + 1);
}
String oldUID = message.getUid();
@ -1592,6 +1628,10 @@ public class LocalStore extends Store implements Serializable
{
setUnreadMessageCount(getUnreadMessageCount() - 1);
}
if (oldMessage != null && oldMessage.isSet(Flag.FLAGGED) == true)
{
setFlaggedMessageCount(getFlaggedMessageCount() - 1);
}
/*
* The message may already exist in this Folder, so delete it first.
*/
@ -1670,6 +1710,10 @@ public class LocalStore extends Store implements Serializable
{
setUnreadMessageCount(getUnreadMessageCount() + 1);
}
if (message.isSet(Flag.FLAGGED) == true)
{
setFlaggedMessageCount(getFlaggedMessageCount() + 1);
}
}
catch (Exception e)
{
@ -1970,14 +2014,15 @@ public class LocalStore extends Store implements Serializable
{
Long.toString(mFolderId), new Long(cutoff)
});
resetUnreadCount();
resetUnreadAndFlaggedCounts();
}
private void resetUnreadCount()
private void resetUnreadAndFlaggedCounts()
{
try
{
int newUnread = 0;
int newFlagged = 0;
Message[] messages = getMessages(null);
for (Message message : messages)
{
@ -1985,14 +2030,20 @@ public class LocalStore extends Store implements Serializable
{
newUnread++;
}
if (message.isSet(Flag.FLAGGED) == true)
{
newFlagged++;
}
}
setUnreadMessageCount(newUnread);
setFlaggedMessageCount(newFlagged);
}
catch (Exception e)
{
Log.e(K9.LOG_TAG, "Unable to fetch all messages from LocalStore", e);
}
}
@Override
public void delete(boolean recurse) throws MessagingException
@ -2539,6 +2590,30 @@ public class LocalStore extends Store implements Serializable
folder.setUnreadMessageCount(folder.getUnreadMessageCount() + 1);
}
}
if ((flag == Flag.DELETED || flag == Flag.X_DESTROYED) && isSet(Flag.FLAGGED))
{
LocalFolder folder = (LocalFolder)mFolder;
if (set)
{
folder.setFlaggedMessageCount(folder.getFlaggedMessageCount() - 1);
}
else
{
folder.setFlaggedMessageCount(folder.getFlaggedMessageCount() + 1);
}
}
if (flag == Flag.FLAGGED && !isSet(Flag.DELETED))
{
LocalFolder folder = (LocalFolder)mFolder;
if (set)
{
folder.setFlaggedMessageCount(folder.getFlaggedMessageCount() + 1);
}
else
{
folder.setFlaggedMessageCount(folder.getFlaggedMessageCount() - 1);
}
}
}
catch (MessagingException me)
{

View File

@ -415,6 +415,11 @@ public class Pop3Store extends Store
{
return -1;
}
@Override
public int getFlaggedMessageCount() throws MessagingException
{
return -1;
}
@Override
public Message getMessage(String uid) throws MessagingException

View File

@ -1332,6 +1332,11 @@ public class WebDavStore extends Store
return this.mUnreadMessageCount;
}
@Override
public int getFlaggedMessageCount() throws MessagingException
{
return -1;
}
@Override
public boolean isOpen()