From 3e4843e756526e9a93697f152f61ebf7a7d6aa89 Mon Sep 17 00:00:00 2001 From: Daniel Applebaum Date: Tue, 10 Feb 2009 03:18:42 +0000 Subject: [PATCH] Issue 131 Provide two options for each Account: Compact (which VACUUMs the accounts and prunes recreatable attachments) Clear all data (danger!) (which wipes all messages and attachments in the account, except placeholder deleted messages) Displays a Toast when the Compact or Clear is queued, and another Toast with the shrinkage results. (Also, a small change to increase probability of seeing messages after a sync, by setting the needsRefresh on all folders when a sync is done.) --- res/drawable/ic_menu_clear.png | Bin 0 -> 2074 bytes res/drawable/ic_menu_compact.png | Bin 0 -> 2074 bytes res/menu/accounts_context.xml | 5 + res/menu/folder_message_list_option.xml | 10 + res/values/strings.xml | 18 ++ .../android/email/MessagingController.java | 58 ++++++ src/com/android/email/MessagingListener.java | 8 + src/com/android/email/activity/Accounts.java | 81 ++++++++ .../email/activity/FolderMessageList.java | 106 ++++++++++- .../android/email/mail/store/LocalStore.java | 175 ++++++++++++++---- 10 files changed, 417 insertions(+), 44 deletions(-) create mode 100644 res/drawable/ic_menu_clear.png create mode 100644 res/drawable/ic_menu_compact.png diff --git a/res/drawable/ic_menu_clear.png b/res/drawable/ic_menu_clear.png new file mode 100644 index 0000000000000000000000000000000000000000..ef076b6933203fe372eb8583162191773fd75939 GIT binary patch literal 2074 zcmV+#2<7*QP)yUh(AL&QTU*<*fq!N# zUlbclF7 zPSZlIz#67jJ>d$w^YF6dN{dAdyHE+*eamgQ_w<9L!6hNJj+E zDi5Buo-D@{gsIUf`*n8S2|z`4Mb@1qB_%i<4ouVJ-o1MqJ$jTACr&J#Lh*PUx7&?j z7$lR)%zQ8yBp3`5>4@M9_=sr?(`oW>=pmc7Y~uBwzm8*-BMne(;3Z2URm#fB@OV7b z*4Es^~SW=A?Alu9Wip{%S7(=^$* zaU-djlupk~lMi@$M8~6)mX@-4^JYxbWMpK7l9Cb(!yq1y6OYH~>gpm82ox7arBvwY z>0x|)Jc~YioA>hNf4`)rriOLv)=@{D-v7`2jG(@NPx1+!a-=9PFDLdm#@OH(JKx!v zS-W-X7WePpCzVPuIXTJL*x3Bo<}0V8(I}mN=*)Z{of^$rcez}+UG6M?`vN{Glwo}V zACZoTCXJ*x%_q7Wc|1aUd;6TweeK#chK7b18yn-;v13cKKzVsNo40OeWMqWd*;#6+ z$+s9sWYq>&^Ri_O`$e*JoSdwYq;<5X5wF5Lp@aplStCMPF}#bQLG(F~0Y z!yqx8Pz(IKz=sQ9q!#jtd=^`lT4>+CeXLuz4u`{m!{IwrBWD4!+ybph%^Av1zI3CHz**BQNp6C(c$4?rl+Sd zO%si&8cAbNME)uwABzZp2RO0_ji-SNOB{<5AdyJ0%CSm`qayNHL_V}E_)FWiBLF>L z^~?vlXyA($ARG*9RaupaNI&qNs@AJ&r-=N_2B=$A8&&nNjhgu~anZmRoXEn#umXt4 zT_7|Sp9;HNE=siI2iBY&Pzcy9B1cs9Ora8DIfrurz;CK*$QSUPZ*OnM?RMkBr3k2M zwTS#8!wHd(EbF|q^TnONE(Uz0BQhsBE{Hy{N1~E@viokSCmw! z6@^MgWE{AVK70F%+o)>D?h8u^scG=@0iR<5o5n8zVc_pTgR1Tg2gCma8i90S@|=jo zt-q^*>2&BjaNxkXUE6oPud1(Dk!dlxfQ16jOCjqAe`bXXcl+AWsM{AbkFtxAFJwZ;5XL(^S~>W?EGkyn9FZELSt{C>?Ib%Bto-c38Zy_vOQ0%SS~A~Ti{zqP=As;X~W7TIH+nFd0Z z&-B~76p@YyyPI~?eY1OI1;|6A_f+*w5%~&u!2&oSA|Xq_$8&zl!j_z#wn6@Vt616;XMu1q zoB?J3q_vm}?2~_d0xpOXe|m{w;BWP3&h2g9yK({)s&{e)0_g4QC7Dce>Cz=!xH4Vq z)vH&NRzwaJ<=YlOLZ?F+!0C!IO`Ehv{jGj3U%t$}JNL3X%XL*uJetUVap6jIS=%a3 zJ6&=QMmi!HKgeAR2g6J}n#jxlMV8UUSwK}Y8n=qKyxp{1>*NBqJNMP@tJyp?3@%)_ zka=k>rruxld!SHjFgo&kKeiG|EBP`l60B3GndW*X=e@x!v*=h z)PIStNG+81iG1}HU3ur0N>&9KlCS@_mKHYo&W#<07*qoM6N<$ Ef*>31Z2$lO literal 0 HcmV?d00001 diff --git a/res/drawable/ic_menu_compact.png b/res/drawable/ic_menu_compact.png new file mode 100644 index 0000000000000000000000000000000000000000..ef076b6933203fe372eb8583162191773fd75939 GIT binary patch literal 2074 zcmV+#2<7*QP)yUh(AL&QTU*<*fq!N# zUlbclF7 zPSZlIz#67jJ>d$w^YF6dN{dAdyHE+*eamgQ_w<9L!6hNJj+E zDi5Buo-D@{gsIUf`*n8S2|z`4Mb@1qB_%i<4ouVJ-o1MqJ$jTACr&J#Lh*PUx7&?j z7$lR)%zQ8yBp3`5>4@M9_=sr?(`oW>=pmc7Y~uBwzm8*-BMne(;3Z2URm#fB@OV7b z*4Es^~SW=A?Alu9Wip{%S7(=^$* zaU-djlupk~lMi@$M8~6)mX@-4^JYxbWMpK7l9Cb(!yq1y6OYH~>gpm82ox7arBvwY z>0x|)Jc~YioA>hNf4`)rriOLv)=@{D-v7`2jG(@NPx1+!a-=9PFDLdm#@OH(JKx!v zS-W-X7WePpCzVPuIXTJL*x3Bo<}0V8(I}mN=*)Z{of^$rcez}+UG6M?`vN{Glwo}V zACZoTCXJ*x%_q7Wc|1aUd;6TweeK#chK7b18yn-;v13cKKzVsNo40OeWMqWd*;#6+ z$+s9sWYq>&^Ri_O`$e*JoSdwYq;<5X5wF5Lp@aplStCMPF}#bQLG(F~0Y z!yqx8Pz(IKz=sQ9q!#jtd=^`lT4>+CeXLuz4u`{m!{IwrBWD4!+ybph%^Av1zI3CHz**BQNp6C(c$4?rl+Sd zO%si&8cAbNME)uwABzZp2RO0_ji-SNOB{<5AdyJ0%CSm`qayNHL_V}E_)FWiBLF>L z^~?vlXyA($ARG*9RaupaNI&qNs@AJ&r-=N_2B=$A8&&nNjhgu~anZmRoXEn#umXt4 zT_7|Sp9;HNE=siI2iBY&Pzcy9B1cs9Ora8DIfrurz;CK*$QSUPZ*OnM?RMkBr3k2M zwTS#8!wHd(EbF|q^TnONE(Uz0BQhsBE{Hy{N1~E@viokSCmw! z6@^MgWE{AVK70F%+o)>D?h8u^scG=@0iR<5o5n8zVc_pTgR1Tg2gCma8i90S@|=jo zt-q^*>2&BjaNxkXUE6oPud1(Dk!dlxfQ16jOCjqAe`bXXcl+AWsM{AbkFtxAFJwZ;5XL(^S~>W?EGkyn9FZELSt{C>?Ib%Bto-c38Zy_vOQ0%SS~A~Ti{zqP=As;X~W7TIH+nFd0Z z&-B~76p@YyyPI~?eY1OI1;|6A_f+*w5%~&u!2&oSA|Xq_$8&zl!j_z#wn6@Vt616;XMu1q zoB?J3q_vm}?2~_d0xpOXe|m{w;BWP3&h2g9yK({)s&{e)0_g4QC7Dce>Cz=!xH4Vq z)vH&NRzwaJ<=YlOLZ?F+!0C!IO`Ehv{jGj3U%t$}JNL3X%XL*uJetUVap6jIS=%a3 zJ6&=QMmi!HKgeAR2g6J}n#jxlMV8UUSwK}Y8n=qKyxp{1>*NBqJNMP@tJyp?3@%)_ zka=k>rruxld!SHjFgo&kKeiG|EBP`l60B3GndW*X=e@x!v*=h z)PIStNG+81iG1}HU3ur0N>&9KlCS@_mKHYo&W#<07*qoM6N<$ Ef*>31Z2$lO literal 0 HcmV?d00001 diff --git a/res/menu/accounts_context.xml b/res/menu/accounts_context.xml index 40139a9d0..e1f569466 100644 --- a/res/menu/accounts_context.xml +++ b/res/menu/accounts_context.xml @@ -6,8 +6,13 @@ android:title="@string/empty_trash_action" /> + + + diff --git a/res/menu/folder_message_list_option.xml b/res/menu/folder_message_list_option.xml index 1c5800848..7417f4ff0 100644 --- a/res/menu/folder_message_list_option.xml +++ b/res/menu/folder_message_list_option.xml @@ -65,6 +65,16 @@ android:title="@string/refresh_action" android:icon="@drawable/ic_menu_refresh" /> + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 201815f3a..14da92ef2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -78,6 +78,21 @@ Load up to %d more + + GB + MB + KB + B + + + Account \"%s\" shrunk from + %s + to + %s + + + Compacting account \"%s\" + Clearing account \"%s\" New email New email from %s @@ -254,6 +269,9 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based WebDav(Exchange) before SMTP Account options + + Compact + Clear all data (danger!) Email checking frequency diff --git a/src/com/android/email/MessagingController.java b/src/com/android/email/MessagingController.java index 2637d2aac..1aa5194ba 100644 --- a/src/com/android/email/MessagingController.java +++ b/src/com/android/email/MessagingController.java @@ -2487,7 +2487,65 @@ s * critical data as fast as possible, and then we'll fill in the de } }); } + + public void compact(final Account account, final MessagingListener ml) + { + putBackground("compact:" + account.getDescription(), ml, new Runnable() + { + public void run() + { + try + { + LocalStore localStore = (LocalStore)Store.getInstance(account.getLocalStoreUri(), mApplication); + long oldSize = localStore.getSize(); + localStore.compact(); + long newSize = localStore.getSize(); + if (ml != null) + { + ml.accountSizeChanged(account, oldSize, newSize); + } + for (MessagingListener l : getListeners()) { + l.accountSizeChanged(account, oldSize, newSize); + l.accountReset(account); + } + } + catch (Exception e) + { + Log.e(Email.LOG_TAG, "Failed to compact account " + account.getDescription(), e); + } + } + }); + } + public void clear(final Account account, final MessagingListener ml) + { + putBackground("clear:" + account.getDescription(), ml, new Runnable() + { + public void run() + { + try + { + LocalStore localStore = (LocalStore)Store.getInstance(account.getLocalStoreUri(), mApplication); + long oldSize = localStore.getSize(); + localStore.clear(); + localStore.resetVisibleLimits(account.getDisplayCount()); + long newSize = localStore.getSize(); + if (ml != null) + { + ml.accountSizeChanged(account, oldSize, newSize); + } + for (MessagingListener l : getListeners()) { + l.accountSizeChanged(account, oldSize, newSize); + l.accountReset(account); + } + } + catch (Exception e) + { + Log.e(Email.LOG_TAG, "Failed to compact account " + account.getDescription(), e); + } + } + }); + } public void saveDraft(final Account account, final Message message) { try { Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication); diff --git a/src/com/android/email/MessagingListener.java b/src/com/android/email/MessagingListener.java index 3f561568a..5edbe4f3e 100644 --- a/src/com/android/email/MessagingListener.java +++ b/src/com/android/email/MessagingListener.java @@ -18,6 +18,14 @@ public class MessagingListener { public void accountStatusChanged(Account account, int unreadMessageCount) { } + + public void accountSizeChanged(Account account, long oldSize, long newSize) + { + } + + public void accountReset(Account account) { + + } public void listFoldersStarted(Account account) { } diff --git a/src/com/android/email/activity/Accounts.java b/src/com/android/email/activity/Accounts.java index 2ed5fff36..bc31334b0 100644 --- a/src/com/android/email/activity/Accounts.java +++ b/src/com/android/email/activity/Accounts.java @@ -29,6 +29,7 @@ import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; +import android.widget.Toast; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; @@ -64,9 +65,17 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli private AccountsHandler mHandler = new AccountsHandler(); private AccountsAdapter mAdapter; + private class AccountSizeChangedHolder { + Account account; + long oldSize; + long newSize; + } + class AccountsHandler extends Handler { private static final int DATA_CHANGED = 1; + private static final int MSG_ACCOUNT_SIZE_CHANGED = 2; + private static final int MSG_WORKING_ACCOUNT = 3; public void handleMessage(android.os.Message msg) { @@ -78,6 +87,29 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli mAdapter.notifyDataSetChanged(); } break; + case MSG_WORKING_ACCOUNT: + { + Account account = (Account)msg.obj; + int res = msg.arg1; + String toastText = getString(res, account.getDescription()); + + Toast toast = Toast.makeText(getApplication(), toastText, Toast.LENGTH_SHORT); + toast.show(); + break; + } + case MSG_ACCOUNT_SIZE_CHANGED: + { + AccountSizeChangedHolder holder = (AccountSizeChangedHolder)msg.obj; + Account account = holder.account; + Long oldSize = holder.oldSize; + Long newSize = holder.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(); + break; + } default: super.handleMessage(msg); } @@ -87,6 +119,28 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli { sendEmptyMessage(DATA_CHANGED); } + + public void workingAccount(Account account, int res) + { + android.os.Message msg = new android.os.Message(); + msg.what = MSG_WORKING_ACCOUNT; + msg.obj = account; + msg.arg1 = res; + + sendMessage(msg); + } + + public void accountSizeChanged(Account account, long oldSize, long newSize) + { + android.os.Message msg = new android.os.Message(); + msg.what = MSG_ACCOUNT_SIZE_CHANGED; + AccountSizeChangedHolder holder = new AccountSizeChangedHolder(); + holder.account = account; + holder.oldSize = oldSize; + holder.newSize = newSize; + msg.obj = holder; + sendMessage(msg); + } } @@ -98,6 +152,14 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli mHandler.dataChanged(); } + @Override + public void accountSizeChanged(Account account, long oldSize, long newSize) + { + + mHandler.accountSizeChanged(account, oldSize, newSize); + + } + @Override public void synchronizeMailboxFinished( Account account, @@ -275,9 +337,28 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli case R.id.empty_trash: onEmptyTrash(account); break; + case R.id.compact: + onCompact(account); + break; + case R.id.clear: + onClear(account); + break; } return true; } + + private void onCompact(Account account) + { + mHandler.workingAccount(account, R.string.compacting_account); + MessagingController.getInstance(getApplication()).compact(account, null); + } + + private void onClear(Account account) + { + mHandler.workingAccount(account, R.string.clearing_account); + MessagingController.getInstance(getApplication()).clear(account, null); + } + public void onItemClick(AdapterView parent, View view, int position, long id) { Account account = (Account)parent.getItemAtPosition(position); diff --git a/src/com/android/email/activity/FolderMessageList.java b/src/com/android/email/activity/FolderMessageList.java index 6ce637ffb..ac7ef335c 100644 --- a/src/com/android/email/activity/FolderMessageList.java +++ b/src/com/android/email/activity/FolderMessageList.java @@ -191,6 +191,8 @@ public class FolderMessageList extends ExpandableListActivity dateFormat = null; timeFormat = null; } + + class FolderMessageListHandler extends Handler { @@ -211,6 +213,10 @@ public class FolderMessageList extends ExpandableListActivity private static final int MSG_FOLDER_SYNCING = 18; private static final int MSG_SENDING_OUTBOX = 19; + + private static final int MSG_ACCOUNT_SIZE_CHANGED = 20; + + private static final int MSG_WORKING_ACCOUNT = 21; @Override public void handleMessage(android.os.Message msg) { @@ -249,6 +255,27 @@ public class FolderMessageList extends ExpandableListActivity mAdapter.notifyDataSetChanged(); break; } + case MSG_ACCOUNT_SIZE_CHANGED: + { + Long[] sizes = (Long[])msg.obj; + String toastText = getString(R.string.account_size_changed, mAccount.getDescription(), + SizeFormatter.formatSize(getApplication(), sizes[0]), SizeFormatter.formatSize(getApplication(), sizes[1]));; + + Toast toast = Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG); + toast.show(); + break; + } + case MSG_WORKING_ACCOUNT: + { + int res = msg.arg1; + String toastText = getString(res, mAccount.getDescription()); + + Toast toast = Toast.makeText(getApplication(), toastText, Toast.LENGTH_SHORT); + toast.show(); + break; + } + + case MSG_SYNC_MESSAGES: { FolderInfoHolder folder = (FolderInfoHolder) ((Object[]) msg.obj)[0]; @@ -312,6 +339,15 @@ public class FolderMessageList extends ExpandableListActivity { folder, messages }; sendMessage(msg); } + + public void workingAccount(int res) + { + android.os.Message msg = new android.os.Message(); + msg.what = MSG_WORKING_ACCOUNT; + msg.arg1 = res; + + sendMessage(msg); + } public void removeMessage(FolderInfoHolder folder, MessageInfoHolder message) { @@ -321,6 +357,14 @@ public class FolderMessageList extends ExpandableListActivity { folder, message }; sendMessage(msg); } + + public void accountSizeChanged(long oldSize, long newSize) + { + android.os.Message msg = new android.os.Message(); + msg.what = MSG_ACCOUNT_SIZE_CHANGED; + msg.obj = new Long[] { oldSize, newSize }; + sendMessage(msg); + } public void folderLoading(String folder, boolean loading) { @@ -587,6 +631,8 @@ public class FolderMessageList extends ExpandableListActivity MessagingController.getInstance(getApplication()).addListener( mAdapter.mListener); mAccount.refresh(Preferences.getPreferences(this)); + markAllRefresh(); + onRefresh(false); NotificationManager notifMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); @@ -628,7 +674,8 @@ public class FolderMessageList extends ExpandableListActivity */ int position = mListView.getFlatListPosition(ExpandableListView .getPackedPositionForGroup(groupPosition)); - mListView.setSelectionFromTop(position, 0); + + mListView.setSelectionFromTop(position, 0); } final FolderInfoHolder folder = (FolderInfoHolder) mAdapter @@ -814,6 +861,11 @@ public class FolderMessageList extends ExpandableListActivity } + private void markAllRefresh() + { + mAdapter.mListener.accountReset(mAccount); + } + private void onCycleSort() { SORT_TYPE[] sorts = SORT_TYPE.values(); @@ -975,11 +1027,28 @@ public class FolderMessageList extends ExpandableListActivity case R.id.empty_trash: onEmptyTrash(mAccount); return true; + case R.id.compact: + onCompact(mAccount); + return true; + case R.id.clear: + onClear(mAccount); + return true; default: return super.onOptionsItemSelected(item); } } + + private void onCompact(Account account) + { + mHandler.workingAccount(R.string.compacting_account); + MessagingController.getInstance(getApplication()).compact(account, null); + } + private void onClear(Account account) + { + mHandler.workingAccount(R.string.clearing_account); + MessagingController.getInstance(getApplication()).clear(account, null); + } @Override public boolean onCreateOptionsMenu(Menu menu) @@ -1186,6 +1255,18 @@ public class FolderMessageList extends ExpandableListActivity } mHandler.dataChanged(); } + @Override + public void accountReset(Account account) + { + if (!account.equals(mAccount)) + { + return; + } + for (FolderInfoHolder folder : mFolders) + { + folder.needsRefresh = true; + } + } @Override public void listFolders(Account account, Folder[] folders) @@ -1484,6 +1565,16 @@ public class FolderMessageList extends ExpandableListActivity } mHandler.sendingOutbox(false); } + + public void accountSizeChanged(Account account, long oldSize, long newSize) + { + if (!account.equals(mAccount)) + { + return; + } + mHandler.accountSizeChanged(oldSize, newSize); + + } @Override public void messageUidChanged(Account account, String folder, @@ -1807,19 +1898,20 @@ public class FolderMessageList extends ExpandableListActivity { holder.main.setText(getString(R.string.status_loading_more)); holder.progress.setVisibility(View.VISIBLE); - } else + } + else { if (folder.lastCheckFailed == false) { holder.main.setText(String.format(getString(R.string.load_more_messages_fmt).toString(), mAccount.getDisplayCount())); - } else + } + else { - holder.main.setText(getString(R.string.status_loading_more_failed)); - } + holder.main.setText(getString(R.string.status_loading_more_failed)); + } holder.progress.setVisibility(View.GONE); - } - return view; + } return view; } else { MessageInfoHolder message = (MessageInfoHolder) getChild(groupPosition, diff --git a/src/com/android/email/mail/store/LocalStore.java b/src/com/android/email/mail/store/LocalStore.java index cdd4c1171..05df42aba 100644 --- a/src/com/android/email/mail/store/LocalStore.java +++ b/src/com/android/email/mail/store/LocalStore.java @@ -140,7 +140,88 @@ public class LocalStore extends Store implements Serializable { throw new Error("Database upgrade failed!"); } } + + public long getSize() + { + long attachmentLength = 0; + + File[] files = mAttachmentsDir.listFiles(); + for (File file : files) { + if (file.exists()) { + attachmentLength += file.length(); + } + } + + + File dbFile = new File(mPath); + return dbFile.length() + attachmentLength; + } + + public void compact() throws MessagingException + { + Log.i(Email.LOG_TAG, "Before prune size = " + getSize()); + + pruneCachedAttachments(); + Log.i(Email.LOG_TAG, "After prune / before compaction size = " + getSize()); + + mDb.execSQL("VACUUM"); + Log.i(Email.LOG_TAG, "After compaction size = " + getSize()); + } + + public void clear() throws MessagingException + { + Log.i(Email.LOG_TAG, "Before prune size = " + getSize()); + + pruneCachedAttachments(true); + + Log.i(Email.LOG_TAG, "After prune / before compaction size = " + getSize()); + + Log.i(Email.LOG_TAG, "Before clear folder count = " + getFolderCount()); + Log.i(Email.LOG_TAG, "Before clear message count = " + getMessageCount()); + + Log.i(Email.LOG_TAG, "After prune / before clear size = " + getSize()); + // don't delete messages that are Local, since there is no copy on the server. + // Don't delete deleted messages. They are essentially placeholders for UIDs of messages that have + // been deleted locally. They take up no space, and are indicated with a null date. + mDb.execSQL("DELETE FROM messages WHERE date is not null and uid not like 'Local%'" ); + + compact(); + Log.i(Email.LOG_TAG, "After clear message count = " + getMessageCount()); + + Log.i(Email.LOG_TAG, "After clear size = " + getSize()); + } + + public int getMessageCount() throws MessagingException { + Cursor cursor = null; + try { + cursor = mDb.rawQuery("SELECT COUNT(*) FROM messages", null); + cursor.moveToFirst(); + int messageCount = cursor.getInt(0); + return messageCount; + } + finally { + if (cursor != null) { + cursor.close(); + } + } + } + + public int getFolderCount() throws MessagingException { + Cursor cursor = null; + try { + cursor = mDb.rawQuery("SELECT COUNT(*) FROM folders", null); + cursor.moveToFirst(); + int messageCount = cursor.getInt(0); + return messageCount; + } + finally { + if (cursor != null) { + cursor.close(); + } + } + } + @Override public LocalFolder getFolder(String name) throws MessagingException { return new LocalFolder(name); @@ -204,49 +285,68 @@ public class LocalStore extends Store implements Serializable { } } + public void pruneCachedAttachments() throws MessagingException { + pruneCachedAttachments(false); + } + /** * Deletes all cached attachments for the entire store. */ - public void pruneCachedAttachments() throws MessagingException { + public void pruneCachedAttachments(boolean force) throws MessagingException { + + if (force) + { + ContentValues cv = new ContentValues(); + cv.putNull("content_uri"); + mDb.update("attachments", cv, null, null); + } File[] files = mAttachmentsDir.listFiles(); for (File file : files) { if (file.exists()) { + if (!force) { + Cursor cursor = null; try { - Cursor cursor = null; - try { - cursor = mDb.query( - "attachments", - new String[] { "store_data" }, - "id = ?", - new String[] { file.getName() }, - null, - null, - null); - if (cursor.moveToNext()) { - if (cursor.getString(0) == null) { - /* - * If the attachment has no store data it is not recoverable, so - * we won't delete it. - */ - continue; - } - } + cursor = mDb.query( + "attachments", + new String[] { "store_data" }, + "id = ?", + new String[] { file.getName() }, + null, + null, + null); + if (cursor.moveToNext()) { + if (cursor.getString(0) == null) { + Log.d(Email.LOG_TAG, "Attachment " + file.getAbsolutePath() + " has no store data, not deleting"); + /* + * If the attachment has no store data it is not recoverable, so + * we won't delete it. + */ + continue; + } + } + } + finally { + if (cursor != null) { + cursor.close(); } - finally { - if (cursor != null) { - cursor.close(); - } - } - ContentValues cv = new ContentValues(); - cv.putNull("content_uri"); - mDb.update("attachments", cv, "id = ?", new String[] { file.getName() }); + } + } + if (!force) + { + try + { + ContentValues cv = new ContentValues(); + cv.putNull("content_uri"); + mDb.update("attachments", cv, "id = ?", new String[] { file.getName() }); } catch (Exception e) { - /* - * If the row has gone away before we got to mark it not-downloaded that's - * okay. - */ - } + /* + * If the row has gone away before we got to mark it not-downloaded that's + * okay. + */ + } + } + Log.d(Email.LOG_TAG, "Deleting attachment " + file.getAbsolutePath() + ", which is of size " + file.length()); if (!file.delete()) { file.deleteOnExit(); } @@ -1292,7 +1392,9 @@ public class LocalStore extends Store implements Serializable { /* * Delete all of the messages' content to save space. */ - mDb.execSQL( + ((LocalFolder) mFolder).deleteAttachments(getUid()); + + mDb.execSQL( "UPDATE messages SET " + "subject = NULL, " + "sender_list = NULL, " + @@ -1307,12 +1409,11 @@ public class LocalStore extends Store implements Serializable { new Object[] { mId }); - - ((LocalFolder) mFolder).deleteAttachments(getUid()); - + /* * Delete all of the messages' attachments to save space. */ + // shouldn't the trigger take care of this? -- danapple mDb.execSQL("DELETE FROM attachments WHERE id = ?", new Object[] { mId