From 898f65e0819666e17e1b6ebc1447c72888b60689 Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 4 Feb 2012 14:52:45 +0100 Subject: [PATCH 01/22] Changed the way we set flags to update the original Message object --- src/com/fsck/k9/activity/MessageCompose.java | 2 +- src/com/fsck/k9/activity/MessageList.java | 13 ++- src/com/fsck/k9/activity/MessageView.java | 30 +---- .../k9/controller/MessagingController.java | 109 +++++++++++++----- 4 files changed, 99 insertions(+), 55 deletions(-) diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index a24423eb3..8b85cfd95 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -1611,7 +1611,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc final Account account = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid); final String folderName = mMessageReference.folderName; final String sourceMessageUid = mMessageReference.uid; - MessagingController.getInstance(getApplication()).setFlag(account, folderName, new String[] {sourceMessageUid}, mMessageReference.flag, true); + MessagingController.getInstance(getApplication()).setFlag(account, folderName, sourceMessageUid, mMessageReference.flag, true); } mDraftNeedsSaving = false; diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index 1d2c8f59e..89d1366ef 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -70,6 +70,7 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore.LocalFolder; +import com.fsck.k9.mail.store.LocalStore.LocalMessage; import com.fsck.k9.mail.store.StorageManager; @@ -1384,13 +1385,21 @@ public class MessageList } private void onToggleRead(MessageInfoHolder holder) { - mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.SEEN, !holder.read); + LocalMessage message = holder.message; + Folder folder = message.getFolder(); + Account account = folder.getAccount(); + String folderName = folder.getName(); + mController.setFlag(account, folderName, new Message[] { message }, Flag.SEEN, !holder.read); holder.read = !holder.read; mHandler.sortMessages(); } private void onToggleFlag(MessageInfoHolder holder) { - mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.FLAGGED, !holder.flagged); + LocalMessage message = holder.message; + Folder folder = message.getFolder(); + Account account = folder.getAccount(); + String folderName = folder.getName(); + mController.setFlag(account, folderName, new Message[] { message }, Flag.FLAGGED, !holder.flagged); holder.flagged = !holder.flagged; mHandler.sortMessages(); } diff --git a/src/com/fsck/k9/activity/MessageView.java b/src/com/fsck/k9/activity/MessageView.java index 64f4c9208..221d195c9 100644 --- a/src/com/fsck/k9/activity/MessageView.java +++ b/src/com/fsck/k9/activity/MessageView.java @@ -20,7 +20,6 @@ import com.fsck.k9.crypto.PgpData; import com.fsck.k9.helper.FileBrowserHelper; import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback; import com.fsck.k9.mail.*; -import com.fsck.k9.mail.store.LocalStore.LocalMessage; import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.view.AttachmentView; import com.fsck.k9.view.ToggleScrollView; @@ -729,17 +728,8 @@ public class MessageView extends K9Activity implements OnClickListener { if (mMessage != null) { boolean newState = !mMessage.isSet(Flag.FLAGGED); mController.setFlag(mAccount, mMessage.getFolder().getName(), - new String[] {mMessage.getUid()}, Flag.FLAGGED, newState); - try { - // FIXME: This is a hack to change the flagged state of our message object. We - // can't call Message.setFlag() because that would "adjust" the flagged count - // another time (first time by MessagingController.setFlag(...)). - ((LocalMessage)mMessage).setFlagInternal(Flag.FLAGGED, newState); - - mMessageView.setHeaders(mMessage, mAccount); - } catch (MessagingException me) { - Log.e(K9.LOG_TAG, "Could not set flag on local message", me); - } + new Message[] { mMessage }, Flag.FLAGGED, newState); + mMessageView.setHeaders(mMessage, mAccount); } } @@ -883,18 +873,10 @@ public class MessageView extends K9Activity implements OnClickListener { private void onMarkAsUnread() { if (mMessage != null) { mController.setFlag(mAccount, mMessage.getFolder().getName(), - new String[] { mMessage.getUid() }, Flag.SEEN, false); - try { - // FIXME: This is a hack to mark our message object as unread. We can't call - // Message.setFlag() because that would "adjust" the unread count twice. - ((LocalMessage)mMessage).setFlagInternal(Flag.SEEN, false); - - mMessageView.setHeaders(mMessage, mAccount); - String subject = mMessage.getSubject(); - setTitle(subject); - } catch (Exception e) { - Log.e(K9.LOG_TAG, "Unable to unset SEEN flag on message", e); - } + new Message[] { mMessage }, Flag.SEEN, false); + mMessageView.setHeaders(mMessage, mAccount); + String subject = mMessage.getSubject(); + setTitle(subject); } } diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 389e6f2a4..6a9081c28 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -2519,65 +2519,118 @@ public class MessagingController implements Runnable { @Override public void act(final Account account, final Folder folder, final List messages) { - String[] uids = new String[messages.size()]; - for (int i = 0; i < messages.size(); i++) { - uids[i] = messages.get(i).getUid(); - } - setFlag(account, folder.getName(), uids, flag, newState); + setFlag(account, folder.getName(), messages.toArray(EMPTY_MESSAGE_ARRAY), flag, + newState); } }); } - public void setFlag( - final Account account, - final String folderName, - final String[] uids, - final Flag flag, - final boolean newState) { - // TODO: put this into the background, but right now that causes odd behavior - // because the FolderMessageList doesn't have its own cache of the flag states + /** + * Set or remove a flag for a set of messages in a specific folder. + * + *

+ * The {@link Message} objects passed in are updated to reflect the new flag state. + *

+ * + * @param account + * The account the folder containing the messages belongs to. + * @param folderName + * The name of the folder. + * @param messages + * The messages to change the flag for. + * @param flag + * The flag to change. + * @param newState + * {@code true}, if the flag should be set. {@code false} if it should be removed. + */ + public void setFlag(Account account, String folderName, Message[] messages, Flag flag, + boolean newState) { + // TODO: Put this into the background, but right now some callers depend on the message + // objects being modified right after this method returns. Folder localFolder = null; try { Store localStore = account.getLocalStore(); localFolder = localStore.getFolder(folderName); localFolder.open(OpenMode.READ_WRITE); - ArrayList messages = new ArrayList(); - for (String uid : uids) { - // Allows for re-allowing sending of messages that could not be sent - if (flag == Flag.FLAGGED && !newState - && uid != null - && account.getOutboxFolderName().equals(folderName)) { - sendCount.remove(uid); - } - Message msg = localFolder.getMessage(uid); - if (msg != null) { - messages.add(msg); + + // Allows for re-allowing sending of messages that could not be sent + if (flag == Flag.FLAGGED && !newState && + account.getOutboxFolderName().equals(folderName)) { + for (Message message : messages) { + String uid = message.getUid(); + if (uid != null) { + sendCount.remove(uid); + } } } - localFolder.setFlags(messages.toArray(EMPTY_MESSAGE_ARRAY), new Flag[] {flag}, newState); - + // Update the messages in the local store + localFolder.setFlags(messages, new Flag[] {flag}, newState); for (MessagingListener l : getListeners()) { l.folderStatusChanged(account, folderName, localFolder.getUnreadMessageCount()); } + + /* + * Handle the remote side + */ + + // The error folder is always a local folder + // TODO: Skip the remote part for all local-only folders if (account.getErrorFolderName().equals(folderName)) { return; } + String[] uids = new String[messages.length]; + for (int i = 0, end = uids.length; i < end; i++) { + uids[i] = messages[i].getUid(); + } + queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids); processPendingCommands(account); } catch (MessagingException me) { addErrorMessage(account, null, me); - throw new RuntimeException(me); } finally { closeFolder(localFolder); } - }//setMesssageFlag + } + + /** + * Set or remove a flag for a message referenced by message UID. + * + * @param account + * The account the folder containing the message belongs to. + * @param folderName + * The name of the folder. + * @param message + * The message to change the flag for. + * @param flag + * The flag to change. + * @param newState + * {@code true}, if the flag should be set. {@code false} if it should be removed. + */ + public void setFlag(Account account, String folderName, String uid, Flag flag, + boolean newState) { + Folder localFolder = null; + try { + LocalStore localStore = account.getLocalStore(); + localFolder = localStore.getFolder(folderName); + localFolder.open(OpenMode.READ_WRITE); + + Message message = localFolder.getMessage(uid); + setFlag(account, folderName, new Message[] { message }, flag, newState); + + } catch (MessagingException me) { + addErrorMessage(account, null, me); + throw new RuntimeException(me); + } finally { + closeFolder(localFolder); + } + } public void clearAllPending(final Account account) { try { From 7ef5f9d37e44f6b01228b1e1676dd4accf46f9b5 Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 4 Feb 2012 18:07:57 +0100 Subject: [PATCH 02/22] Set flagged and unread count to 0 when emptying the trash folder --- src/com/fsck/k9/controller/MessagingController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 6a9081c28..56b853d21 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -3529,7 +3529,8 @@ public class MessagingController implements Runnable { localFolder = (LocalFolder) localStore.getFolder(account.getTrashFolderName()); localFolder.open(OpenMode.READ_WRITE); localFolder.setFlags(new Flag[] { Flag.DELETED }, true); - localFolder.resetUnreadAndFlaggedCounts(); + localFolder.setUnreadMessageCount(0); + localFolder.setFlaggedMessageCount(0); for (MessagingListener l : getListeners()) { l.emptyTrashCompleted(account); From 700ba1d781935cbc2add56e1dbae7c57a03b0949 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Mon, 6 Feb 2012 23:11:12 -0500 Subject: [PATCH 03/22] Bumped manifest to 4.108 --- AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9b8501028..b619193c3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,8 +1,8 @@ Date: Wed, 1 Feb 2012 00:07:19 -0200 Subject: [PATCH 04/22] Issue 3280: Add launcher shortcuts for special accounts/folders ("Unified Inbox" and "All messages") --- AndroidManifest.xml | 4 ++ .../fsck/k9/activity/LauncherShortcuts.java | 54 ++++++++++++++++--- src/com/fsck/k9/activity/MessageList.java | 15 +++++- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b619193c3..d0f09e32e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -208,6 +208,10 @@ android:launchMode="singleTask" android:configChanges="locale" > + + + + accounts = new ArrayList(); + + if (!K9.isHideSpecialAccounts()) { + BaseAccount integratedInboxAccount = new SearchAccount(this, true, null, null); + integratedInboxAccount.setDescription( + getString(R.string.integrated_inbox_title)); + integratedInboxAccount.setEmail( + getString(R.string.integrated_inbox_detail)); + + BaseAccount unreadAccount = new SearchAccount(this, false, null, null); + unreadAccount.setDescription( + getString(R.string.search_all_messages_title)); + unreadAccount.setEmail( + getString(R.string.search_all_messages_detail)); + + accounts.add(integratedInboxAccount); + accounts.add(unreadAccount); + } + + accounts.addAll(Arrays.asList(Preferences.getPreferences(this).getAccounts())); mAdapter = new AccountsAdapter(accounts); getListView().setAdapter(mAdapter); } - private void setupShortcut(Account account) { - final Intent shortcutIntent = FolderList.actionHandleAccountIntent(this, account, null, true); + private void setupShortcut(BaseAccount account) { + Intent shortcutIntent = null; + + if (account instanceof SearchSpecification) { + shortcutIntent = MessageList.actionHandleAccountIntent( + this, account.getDescription(), (SearchSpecification) account); + } else { + shortcutIntent = FolderList.actionHandleAccountIntent(this, + (Account) account, null, true); + } Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); @@ -65,18 +100,18 @@ public class LauncherShortcuts extends K9ListActivity implements OnItemClickList } public void onItemClick(AdapterView parent, View view, int position, long id) { - Account account = (Account) parent.getItemAtPosition(position); + BaseAccount account = (BaseAccount) parent.getItemAtPosition(position); setupShortcut(account); } - class AccountsAdapter extends ArrayAdapter { - public AccountsAdapter(Account[] accounts) { + class AccountsAdapter extends ArrayAdapter { + public AccountsAdapter(List accounts) { super(LauncherShortcuts.this, 0, accounts); } @Override public View getView(int position, View convertView, ViewGroup parent) { - final Account account = getItem(position); + final BaseAccount account = getItem(position); final View view; if (convertView != null) { @@ -110,7 +145,10 @@ public class LauncherShortcuts extends K9ListActivity implements OnItemClickList holder.description.setText(description); - holder.chip.setBackgroundColor(account.getChipColor()); + if (account instanceof Account) { + holder.chip.setBackgroundColor(((Account) account).getChipColor()); + } + holder.chip.getBackground().setAlpha(255); holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getAccountName()); diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index 89d1366ef..867fb53fe 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -52,10 +52,12 @@ import android.widget.Toast; import com.fsck.k9.Account; import com.fsck.k9.AccountStats; +import com.fsck.k9.BaseAccount; import com.fsck.k9.FontSizes; import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.R; +import com.fsck.k9.SearchAccount; import com.fsck.k9.SearchSpecification; import com.fsck.k9.activity.setup.AccountSettings; import com.fsck.k9.activity.setup.FolderSettings; @@ -614,7 +616,11 @@ public class MessageList context.startActivity(intent); } - public static void actionHandle(Context context, String title, SearchSpecification searchSpecification) { + /** + * Creates and returns an intent that opens Unified Inbox or All Messages screen. + */ + public static Intent actionHandleAccountIntent(Context context, String title, + SearchSpecification searchSpecification) { Intent intent = new Intent(context, MessageList.class); intent.putExtra(EXTRA_QUERY, searchSpecification.getQuery()); if (searchSpecification.getRequiredFlags() != null) { @@ -630,6 +636,13 @@ public class MessageList intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + + return intent; + } + + public static void actionHandle(Context context, String title, + SearchSpecification searchSpecification) { + Intent intent = actionHandleAccountIntent(context, title, searchSpecification); context.startActivity(intent); } From e766f75da2f7cd9eddaa2eae1e3911a4afa14914 Mon Sep 17 00:00:00 2001 From: wilian-cb Date: Sun, 12 Feb 2012 13:29:16 -0200 Subject: [PATCH 05/22] Creating and initializing special accounts in Accounts activity to avoid NullPointerException when it's resumed. Constant added to represent the number of special accounts. --- src/com/fsck/k9/activity/Accounts.java | 36 +++++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index 70206b9b9..d49ee0be0 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -106,6 +106,11 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC */ private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0]; + /** + * Number of special accounts ('Unified Inbox' and 'All Messages') + */ + private static final int SPECIAL_ACCOUNTS_COUNT = 2; + private static final int DIALOG_REMOVE_ACCOUNT = 1; private static final int DIALOG_CLEAR_ACCOUNT = 2; private static final int DIALOG_RECREATE_ACCOUNT = 3; @@ -325,13 +330,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC super.onCreate(icicle); if (!K9.isHideSpecialAccounts()) { - unreadAccount = new SearchAccount(this, false, null, null); - unreadAccount.setDescription(getString(R.string.search_all_messages_title)); - unreadAccount.setEmail(getString(R.string.search_all_messages_detail)); - - integratedInboxAccount = new SearchAccount(this, true, null, null); - integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title)); - integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail)); + createSpecialAccounts(); } Account[] accounts = Preferences.getPreferences(this).getAccounts(); @@ -374,6 +373,19 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC } } + /** + * Creates and initializes the special accounts ('Integrated Inbox' and 'All Messages') + */ + private void createSpecialAccounts() { + unreadAccount = new SearchAccount(this, false, null, null); + unreadAccount.setDescription(getString(R.string.search_all_messages_title)); + unreadAccount.setEmail(getString(R.string.search_all_messages_detail)); + + integratedInboxAccount = new SearchAccount(this, true, null, null); + integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title)); + integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail)); + } + @SuppressWarnings("unchecked") private void restoreAccountStats(Bundle icicle) { if (icicle != null) { @@ -458,9 +470,13 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC accounts = Preferences.getPreferences(this).getAccounts(); List newAccounts; - if (!K9.isHideSpecialAccounts() - && accounts.length > 0) { - newAccounts = new ArrayList(accounts.length + 2); + if (!K9.isHideSpecialAccounts() && accounts.length > 0) { + if (integratedInboxAccount == null || unreadAccount == null) { + createSpecialAccounts(); + } + + newAccounts = new ArrayList(accounts.length + + SPECIAL_ACCOUNTS_COUNT); newAccounts.add(integratedInboxAccount); newAccounts.add(unreadAccount); } else { From 53604be914486e72cf8740833ccea2273ee1566e Mon Sep 17 00:00:00 2001 From: cketti Date: Mon, 13 Feb 2012 00:56:34 +0100 Subject: [PATCH 06/22] Fixed NullPointerException --- src/com/fsck/k9/controller/MessagingController.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 56b853d21..bf766ee4e 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -2622,8 +2622,9 @@ public class MessagingController implements Runnable { localFolder.open(OpenMode.READ_WRITE); Message message = localFolder.getMessage(uid); - setFlag(account, folderName, new Message[] { message }, flag, newState); - + if (message != null) { + setFlag(account, folderName, new Message[] { message }, flag, newState); + } } catch (MessagingException me) { addErrorMessage(account, null, me); throw new RuntimeException(me); From 328701e87e935be49a31f96953d672b590db21bf Mon Sep 17 00:00:00 2001 From: cketti Date: Mon, 13 Feb 2012 00:57:06 +0100 Subject: [PATCH 07/22] Fixed JavaDoc --- src/com/fsck/k9/controller/MessagingController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index bf766ee4e..f4706581a 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -2606,8 +2606,8 @@ public class MessagingController implements Runnable { * The account the folder containing the message belongs to. * @param folderName * The name of the folder. - * @param message - * The message to change the flag for. + * @param uid + * The UID of the message to change the flag for. * @param flag * The flag to change. * @param newState From 29e1a682886d1a86f7cdc37dfb33ce4ef0c9ed42 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 7 Feb 2012 01:00:16 +0100 Subject: [PATCH 08/22] Added widget to display the unread count for an account --- AndroidManifest.xml | 20 +++ res/drawable/rounded_corners.xml | 9 + res/drawable/unread_count_background.xml | 9 + res/layout/account_list.xml | 26 +++ res/layout/launcher_shortcuts.xml | 11 -- res/layout/unread_widget_layout.xml | 52 ++++++ res/values/strings.xml | 2 + res/xml/unread_widget_info.xml | 8 + src/com/fsck/k9/activity/AccountList.java | 163 ++++++++++++++++++ .../fsck/k9/activity/LauncherShortcuts.java | 2 +- .../activity/UnreadWidgetConfiguration.java | 93 ++++++++++ src/com/fsck/k9/mail/store/LocalStore.java | 7 + .../k9/provider/UnreadWidgetProvider.java | 123 +++++++++++++ 13 files changed, 513 insertions(+), 12 deletions(-) create mode 100644 res/drawable/rounded_corners.xml create mode 100644 res/drawable/unread_count_background.xml create mode 100644 res/layout/account_list.xml delete mode 100644 res/layout/launcher_shortcuts.xml create mode 100644 res/layout/unread_widget_layout.xml create mode 100644 res/xml/unread_widget_info.xml create mode 100644 src/com/fsck/k9/activity/AccountList.java create mode 100644 src/com/fsck/k9/activity/UnreadWidgetConfiguration.java create mode 100644 src/com/fsck/k9/provider/UnreadWidgetProvider.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d0f09e32e..f530b902f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -391,5 +391,25 @@ otherwise it would make K-9 start at the wrong time android:readPermission="com.fsck.k9.permission.READ_MESSAGES" android:writePermission="com.fsck.k9.permission.DELETE_MESSAGES" /> + + + + + + + + + + + + + + diff --git a/res/drawable/rounded_corners.xml b/res/drawable/rounded_corners.xml new file mode 100644 index 000000000..73f1772ef --- /dev/null +++ b/res/drawable/rounded_corners.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/res/drawable/unread_count_background.xml b/res/drawable/unread_count_background.xml new file mode 100644 index 000000000..d8504bd43 --- /dev/null +++ b/res/drawable/unread_count_background.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/res/layout/account_list.xml b/res/layout/account_list.xml new file mode 100644 index 000000000..468904618 --- /dev/null +++ b/res/layout/account_list.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/res/layout/launcher_shortcuts.xml b/res/layout/launcher_shortcuts.xml deleted file mode 100644 index f05c883c1..000000000 --- a/res/layout/launcher_shortcuts.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/res/layout/unread_widget_layout.xml b/res/layout/unread_widget_layout.xml new file mode 100644 index 000000000..2b242b1bd --- /dev/null +++ b/res/layout/unread_widget_layout.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 9fd5973aa..79af72444 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1126,4 +1126,6 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin Move down Moving account... + Unread count + Show unread count for… diff --git a/res/xml/unread_widget_info.xml b/res/xml/unread_widget_info.xml new file mode 100644 index 000000000..8e060337d --- /dev/null +++ b/res/xml/unread_widget_info.xml @@ -0,0 +1,8 @@ + + + diff --git a/src/com/fsck/k9/activity/AccountList.java b/src/com/fsck/k9/activity/AccountList.java new file mode 100644 index 000000000..3e8ac0109 --- /dev/null +++ b/src/com/fsck/k9/activity/AccountList.java @@ -0,0 +1,163 @@ +package com.fsck.k9.activity; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import com.fsck.k9.Account; +import com.fsck.k9.FontSizes; +import com.fsck.k9.K9; +import com.fsck.k9.Preferences; +import com.fsck.k9.R; + + +/** + * Activity displaying the list of accounts. + * + *

+ * Classes extending this abstract class have to provide an {@link #onAccountSelected(Account)} + * method to perform an action when an account is selected. + *

+ */ +public abstract class AccountList extends K9ListActivity implements OnItemClickListener { + private FontSizes mFontSizes = K9.getFontSizes(); + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + setResult(RESULT_CANCELED); + + setContentView(R.layout.account_list); + + ListView listView = getListView(); + listView.setOnItemClickListener(this); + listView.setItemsCanFocus(false); + } + + /** + * Reload list of accounts when this activity is resumed. + */ + @Override + public void onResume() { + super.onResume(); + new LoadAccounts().execute(); + } + + public void onItemClick(AdapterView parent, View view, int position, long id) { + Account account = (Account) parent.getItemAtPosition(position); + onAccountSelected(account); + } + + /** + * Create a new {@link AccountsAdapter} instance and assign it to the {@link ListView}. + * + * @param accounts + * An array of accounts to display. + */ + public void populateListView(Account[] accounts) { + AccountsAdapter adapter = new AccountsAdapter(accounts); + ListView listView = getListView(); + listView.setAdapter(adapter); + listView.invalidate(); + } + + /** + * This method will be called when an account was selected. + * + * @param account + * The account the user selected. + */ + protected abstract void onAccountSelected(Account account); + + class AccountsAdapter extends ArrayAdapter { + private Account[] mAccounts; + + public AccountsAdapter(Account[] accounts) { + super(AccountList.this, 0, accounts); + mAccounts = accounts; + } + + public Account[] getAccounts() { + return mAccounts; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final Account account = getItem(position); + + final View view; + if (convertView != null) { + view = convertView; + } else { + view = getLayoutInflater().inflate(R.layout.accounts_item, parent, false); + view.findViewById(R.id.active_icons).setVisibility(View.GONE); + view.findViewById(R.id.folders).setVisibility(View.GONE); + view.getBackground().setAlpha(0); + } + + AccountViewHolder holder = (AccountViewHolder) view.getTag(); + if (holder == null) { + holder = new AccountViewHolder(); + holder.description = (TextView) view.findViewById(R.id.description); + holder.email = (TextView) view.findViewById(R.id.email); + holder.chip = view.findViewById(R.id.chip); + + view.setTag(holder); + } + + String description = account.getDescription(); + if (account.getEmail().equals(description)) { + holder.email.setVisibility(View.GONE); + } else { + holder.email.setVisibility(View.VISIBLE); + holder.email.setText(account.getEmail()); + } + + if (description == null || description.length() == 0) { + description = account.getEmail(); + } + + holder.description.setText(description); + + holder.chip.setBackgroundColor(account.getChipColor()); + holder.chip.getBackground().setAlpha(255); + + holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP, + mFontSizes.getAccountName()); + holder.email.setTextSize(TypedValue.COMPLEX_UNIT_SP, + mFontSizes.getAccountDescription()); + + return view; + } + + class AccountViewHolder { + public TextView description; + public TextView email; + public View chip; + } + } + + /** + * Load accounts in a background thread + */ + class LoadAccounts extends AsyncTask { + @Override + protected Account[] doInBackground(Void... params) { + Account[] accounts = Preferences.getPreferences(getApplicationContext()).getAccounts(); + return accounts; + } + + @Override + protected void onPostExecute(Account[] accounts) { + populateListView(accounts); + } + } +} diff --git a/src/com/fsck/k9/activity/LauncherShortcuts.java b/src/com/fsck/k9/activity/LauncherShortcuts.java index 580d5e342..aaf2008da 100644 --- a/src/com/fsck/k9/activity/LauncherShortcuts.java +++ b/src/com/fsck/k9/activity/LauncherShortcuts.java @@ -40,7 +40,7 @@ public class LauncherShortcuts extends K9ListActivity implements OnItemClickList return; } - setContentView(R.layout.launcher_shortcuts); + setContentView(R.layout.account_list); ListView listView = getListView(); listView.setOnItemClickListener(this); listView.setItemsCanFocus(false); diff --git a/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java b/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java new file mode 100644 index 000000000..482a51949 --- /dev/null +++ b/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java @@ -0,0 +1,93 @@ +package com.fsck.k9.activity; + +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Bundle; + +import com.fsck.k9.Account; +import com.fsck.k9.R; +import com.fsck.k9.provider.UnreadWidgetProvider; + + +/** + * Activity to select an account for the unread widget. + */ +public class UnreadWidgetConfiguration extends AccountList { + /** + * Name of the preference file to store the widget configuration. + */ + private static final String PREFS_NAME = "unread_widget_configuration.xml"; + + /** + * Prefix for the preference keys. + */ + private static final String PREF_PREFIX_KEY = "unread_widget."; + + + /** + * The ID of the widget we are configuring. + */ + private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Find the widget ID from the intent. + Intent intent = getIntent(); + Bundle extras = intent.getExtras(); + if (extras != null) { + mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID); + } + + // If they gave us an intent without the widget ID, just bail. + if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish(); + return; + } + + setTitle(R.string.unread_widget_select_account); + } + + @Override + protected void onAccountSelected(Account account) { + // Save widget configuration + String accountUuid = account.getUuid(); + saveAccountUuid(this, mAppWidgetId, accountUuid); + + // Update widget + Context context = getApplicationContext(); + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + UnreadWidgetProvider.updateWidget(context, appWidgetManager, mAppWidgetId, accountUuid); + + // Let the caller know that the configuration was successful + Intent resultValue = new Intent(); + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); + setResult(RESULT_OK, resultValue); + finish(); + } + + private static void saveAccountUuid(Context context, int appWidgetId, String accountUuid) { + SharedPreferences.Editor editor = + context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit(); + editor.putString(PREF_PREFIX_KEY + appWidgetId, accountUuid); + editor.commit(); + } + + public static String getAccountUuid(Context context, int appWidgetId) { + SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + String accountUuid = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null); + return accountUuid; + } + + public static void deleteWidgetConfiguration(Context context, int appWidgetId) { + Editor editor = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit(); + editor.remove(PREF_PREFIX_KEY + appWidgetId); + editor.commit(); + } +} diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index 049f4222e..ad8524090 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -60,6 +60,7 @@ import com.fsck.k9.mail.store.LockableDatabase.DbCallback; import com.fsck.k9.mail.store.LockableDatabase.WrappedException; import com.fsck.k9.mail.store.StorageManager.StorageProvider; import com.fsck.k9.provider.AttachmentProvider; +import com.fsck.k9.provider.UnreadWidgetProvider; /** *
@@ -499,6 +500,9 @@ public class LocalStore extends Store implements Serializable {
             public Void doDbWork(final SQLiteDatabase db) {
                 db.execSQL("DELETE FROM messages WHERE deleted = 0 and uid not like 'Local%'");
                 db.execSQL("update folders set flagged_count = 0, unread_count = 0");
+
+                // FIXME: hack to update unread count widget
+                UnreadWidgetProvider.updateUnreadCount(K9.app);
                 return null;
             }
         });
@@ -1311,6 +1315,9 @@ public class LocalStore extends Store implements Serializable {
         public void setUnreadMessageCount(final int unreadMessageCount) throws MessagingException {
             mUnreadMessageCount = Math.max(0, unreadMessageCount);
             updateFolderColumn("unread_count", mUnreadMessageCount);
+
+            // FIXME: hack to update unread count widget
+            UnreadWidgetProvider.updateUnreadCount(K9.app);
         }
 
         public void setFlaggedMessageCount(final int flaggedMessageCount) throws MessagingException {
diff --git a/src/com/fsck/k9/provider/UnreadWidgetProvider.java b/src/com/fsck/k9/provider/UnreadWidgetProvider.java
new file mode 100644
index 000000000..8913fb4e7
--- /dev/null
+++ b/src/com/fsck/k9/provider/UnreadWidgetProvider.java
@@ -0,0 +1,123 @@
+package com.fsck.k9.provider;
+
+import com.fsck.k9.Account;
+import com.fsck.k9.AccountStats;
+import com.fsck.k9.K9;
+import com.fsck.k9.Preferences;
+import com.fsck.k9.R;
+import com.fsck.k9.activity.UnreadWidgetConfiguration;
+import com.fsck.k9.activity.FolderList;
+import com.fsck.k9.activity.MessageList;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.widget.RemoteViews;
+
+public class UnreadWidgetProvider extends AppWidgetProvider {
+
+    /**
+     * Trigger update for all of our unread widgets.
+     *
+     * @param context
+     *         The {@code Context} object to use for the broadcast intent.
+     */
+    public static void updateUnreadCount(Context context) {
+        Context appContext = context.getApplicationContext();
+        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(appContext);
+
+        ComponentName thisWidget = new ComponentName(appContext, UnreadWidgetProvider.class);
+        int[] widgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
+
+        Intent intent = new Intent(context, UnreadWidgetProvider.class);
+        intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
+
+        context.sendBroadcast(intent);
+    }
+
+    public static void updateWidget(Context context, AppWidgetManager appWidgetManager,
+            int appWidgetId, String accountUuid) {
+
+        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
+                R.layout.unread_widget_layout);
+
+        int unreadCount = 0;
+        String accountName = context.getString(R.string.app_name);
+        Intent clickIntent = null;
+        try {
+            Account account = Preferences.getPreferences(context).getAccount(accountUuid);
+            if (account != null) {
+                AccountStats stats = new AccountStats();
+                account.getLocalStore().getMessageCounts(stats);
+                unreadCount = stats.unreadMessageCount;
+                accountName = account.getDescription();
+                if (K9.FOLDER_NONE.equals(account.getAutoExpandFolderName())) {
+                    clickIntent = FolderList.actionHandleAccountIntent(context, account, null);
+                } else {
+                    clickIntent = MessageList.actionHandleFolderIntent(context, account,
+                            account.getAutoExpandFolderName());
+                }
+                clickIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+            }
+        } catch (Exception e) {
+            if (K9.DEBUG) {
+                Log.e(K9.LOG_TAG, "Error getting widget configuration", e);
+            }
+        }
+
+        if (unreadCount == 0) {
+            // Hide TextView for unread count if there are no unread messages.
+            remoteViews.setViewVisibility(R.id.unread_count, View.GONE);
+        } else {
+            remoteViews.setTextViewText(R.id.unread_count, String.valueOf(unreadCount));
+        }
+
+        remoteViews.setTextViewText(R.id.account_name, accountName);
+
+        if (clickIntent == null) {
+            // If the widget configuration couldn't be loaded we open the configuration
+            // activity when the user clicks the widget.
+            clickIntent = new Intent(context, UnreadWidgetConfiguration.class);
+            clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+        }
+        clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+
+        PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId,
+                clickIntent, 0);
+
+        remoteViews.setOnClickPendingIntent(R.id.unread_widget_layout, pendingIntent);
+
+        appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
+
+    }
+
+
+    /**
+     * Called when one or more widgets need to be updated.
+     */
+    @Override
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        for (int widgetId : appWidgetIds) {
+            String accountUuid = UnreadWidgetConfiguration.getAccountUuid(context, widgetId);
+
+            updateWidget(context, appWidgetManager, widgetId, accountUuid);
+        }
+    }
+
+    /**
+     * Called when a widget instance is deleted.
+     */
+    @Override
+    public void onDeleted(Context context, int[] appWidgetIds) {
+        for (int appWidgetId : appWidgetIds) {
+            UnreadWidgetConfiguration.deleteWidgetConfiguration(context, appWidgetId);
+        }
+    }
+}

From e87f4cd98a064c2f669d0d01c7d065cb5c817142 Mon Sep 17 00:00:00 2001
From: cketti 
Date: Tue, 7 Feb 2012 15:18:21 +0100
Subject: [PATCH 09/22] Code cleanup. No functional changes.

---
 .../fsck/k9/controller/MessagingListener.java | 201 ++++++++----------
 1 file changed, 83 insertions(+), 118 deletions(-)

diff --git a/src/com/fsck/k9/controller/MessagingListener.java b/src/com/fsck/k9/controller/MessagingListener.java
index bdf35fa00..f9ae2c89c 100644
--- a/src/com/fsck/k9/controller/MessagingListener.java
+++ b/src/com/fsck/k9/controller/MessagingListener.java
@@ -12,191 +12,156 @@ import com.fsck.k9.mail.Part;
 import java.util.List;
 
 /**
- * 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
- * do so without implementing the entire interface. It is highly recommended that users of
- * this interface use the @Override annotation in their implementations to avoid being caught by
+ * Defines the interface that {@link 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 do so without implementing the entire interface. It is highly recommended that users of this + * interface use the {@code @Override} annotation in their implementations to avoid being caught by * changes in this class. + *

*/ public class MessagingListener { public void searchStats(AccountStats stats) {} - public void accountStatusChanged(BaseAccount account, AccountStats stats) { - } - public void accountSizeChanged(Account account, long oldSize, long newSize) { - } + public void accountStatusChanged(BaseAccount account, AccountStats stats) {} - public void listFoldersStarted(Account account) { - } + public void accountSizeChanged(Account account, long oldSize, long newSize) {} - public void listFolders(Account account, Folder[] folders) { - } - public void listFoldersFailed(Account account, String message) { - } + public void listFoldersStarted(Account account) {} - public void listFoldersFinished(Account account) { - } + public void listFolders(Account account, Folder[] folders) {} - public void listLocalMessagesStarted(Account account, String folder) { - } + public void listFoldersFinished(Account account) {} - public void listLocalMessages(Account account, String folder, Message[] messages) { - } + public void listFoldersFailed(Account account, String message) {} - public void listLocalMessagesAddMessages(Account account, String folder, List messages) { - } - public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) { - } + public void listLocalMessagesStarted(Account account, String folder) {} - public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) { - } + public void listLocalMessages(Account account, String folder, Message[] messages) {} - public void listLocalMessagesFailed(Account account, String folder, String message) { - } + public void listLocalMessagesAddMessages(Account account, String folder, + List messages) {} - public void listLocalMessagesFinished(Account account, String folder) { - } + public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {} - public void synchronizeMailboxStarted(Account account, String folder) { - } + public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {} - public void synchronizeMailboxHeadersStarted(Account account, String folder) { - } + public void listLocalMessagesFinished(Account account, String folder) {} - public void synchronizeMailboxHeadersProgress(Account account, String folder, int completed, int total) { - } + public void listLocalMessagesFailed(Account account, String folder, String message) {} + + + public void synchronizeMailboxStarted(Account account, String folder) {} + + public void synchronizeMailboxHeadersStarted(Account account, String folder) {} + + public void synchronizeMailboxHeadersProgress(Account account, String folder, + int completed, int total) {} public void synchronizeMailboxHeadersFinished(Account account, String folder, - int totalMessagesInMailbox, int numNewMessages) { - } + int totalMessagesInMailbox, int numNewMessages) {} + public void synchronizeMailboxProgress(Account account, String folder, int completed, + int total) {} - public void synchronizeMailboxProgress(Account account, String folder, int completed, int total) - {} + public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {} - public void synchronizeMailboxNewMessage(Account account, String folder, Message message) { - } + public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, + Message message) {} - public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) { - } - - public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) { - } + public void synchronizeMailboxRemovedMessage(Account account, String folder, + Message message) {} public void synchronizeMailboxFinished(Account account, String folder, - int totalMessagesInMailbox, int numNewMessages) { - } + int totalMessagesInMailbox, int numNewMessages) {} - public void synchronizeMailboxFailed(Account account, String folder, - String message) { - } + public void synchronizeMailboxFailed(Account account, String folder, String message) {} - public void loadMessageForViewStarted(Account account, String folder, String uid) { - } + + public void loadMessageForViewStarted(Account account, String folder, String uid) {} public void loadMessageForViewHeadersAvailable(Account account, String folder, String uid, - Message message) { - } + Message message) {} public void loadMessageForViewBodyAvailable(Account account, String folder, String uid, - Message message) { - } + Message message) {} public void loadMessageForViewFinished(Account account, String folder, String uid, - Message message) { - } + Message message) {} - public void loadMessageForViewFailed(Account account, String folder, String uid, Throwable t) { - } + public void loadMessageForViewFailed(Account account, String folder, String uid, + Throwable t) {} /** * Called when a message for view has been fully displayed on the screen. */ public void messageViewFinished() {} - public void checkMailStarted(Context context, Account account) { - } - public void checkMailFinished(Context context, Account account) { - } + public void checkMailStarted(Context context, Account account) {} - public void checkMailFailed(Context context, Account account, String reason) { - } + public void checkMailFinished(Context context, Account account) {} - public void sendPendingMessagesStarted(Account account) { - } + public void checkMailFailed(Context context, Account account, String reason) {} - public void sendPendingMessagesCompleted(Account account) { - } - public void sendPendingMessagesFailed(Account account) { - } + public void sendPendingMessagesStarted(Account account) {} - public void messageDeleted(Account account, String folder, Message message) { + public void sendPendingMessagesCompleted(Account account) {} - } - public void emptyTrashCompleted(Account account) { - } + public void sendPendingMessagesFailed(Account account) {} - public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) { - } - public void folderStatusChanged(Account account, String folderName) { - } + public void emptyTrashCompleted(Account account) {} - public void systemStatusChanged() { - } - public void messageUidChanged(Account account, String folder, String oldUid, String newUid) { + public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) {} - } + public void folderStatusChanged(Account account, String folderName) {} - public void setPushActive(Account account, String folderName, boolean enabled) { - } + public void systemStatusChanged() {} - public void loadAttachmentStarted( - Account account, - Message message, - Part part, - Object tag, - boolean requiresDownload) { - } - public void loadAttachmentFinished( - Account account, - Message message, - Part part, - Object tag) { - } + public void messageDeleted(Account account, String folder, Message message) {} - public void loadAttachmentFailed( - Account account, - Message message, - Part part, - Object tag, - String reason) { - } + public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {} + + + public void setPushActive(Account account, String folderName, boolean enabled) {} + + + public void loadAttachmentStarted(Account account, Message message, Part part, Object tag, + boolean requiresDownload) {} + + public void loadAttachmentFinished(Account account, Message message, Part part, Object tag) {} + + public void loadAttachmentFailed(Account account, Message message, Part part, Object tag, + String reason) {} + + + + public void pendingCommandStarted(Account account, String commandTitle) {} public void pendingCommandsProcessing(Account account) {} - public void pendingCommandsFinished(Account account) {} - public void pendingCommandStarted(Account account, String commandTitle) - {} - public void pendingCommandCompleted(Account account, String commandTitle) - {} + public void pendingCommandCompleted(Account account, String commandTitle) {} + + public void pendingCommandsFinished(Account account) {} + /** * General notification messages subclasses can override to be notified that the controller * has completed a command. This is useful for turning off progress indicators that may have * been left over from previous commands. - * @param moreCommandsToRun True if the controller will continue on to another command - * immediately. + * + * @param moreCommandsToRun + * {@code true} if the controller will continue on to another command immediately. + * {@code false} otherwise. */ - public void controllerCommandCompleted(boolean moreCommandsToRun) { - - } + public void controllerCommandCompleted(boolean moreCommandsToRun) {} } From d4bc664c4111e102650ea9f0b79a242bc05353f7 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 7 Feb 2012 15:26:41 +0100 Subject: [PATCH 10/22] Removed unused method --- src/com/fsck/k9/controller/MessagingListener.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/com/fsck/k9/controller/MessagingListener.java b/src/com/fsck/k9/controller/MessagingListener.java index f9ae2c89c..49ddbf4da 100644 --- a/src/com/fsck/k9/controller/MessagingListener.java +++ b/src/com/fsck/k9/controller/MessagingListener.java @@ -121,8 +121,6 @@ public class MessagingListener { public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) {} - public void folderStatusChanged(Account account, String folderName) {} - public void systemStatusChanged() {} From 98461e5a21507c1968f3b5936a15d0337f2785be Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 7 Feb 2012 16:30:38 +0100 Subject: [PATCH 11/22] Notify listeners if unread count changed due to a copy/move operation --- .../k9/controller/MessagingController.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index f4706581a..1d536f91f 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -3297,12 +3297,17 @@ public class MessagingController implements Runnable { Folder localSrcFolder = localStore.getFolder(srcFolder); Folder localDestFolder = localStore.getFolder(destFolder); + boolean unreadCountAffected = false; List uids = new LinkedList(); for (Message message : inMessages) { String uid = message.getUid(); if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) { uids.add(uid); } + + if (!unreadCountAffected && !message.isSet(Flag.SEEN)) { + unreadCountAffected = true; + } } Message[] messages = localSrcFolder.getMessages(uids.toArray(EMPTY_STRING_ARRAY), null); @@ -3323,6 +3328,15 @@ public class MessagingController implements Runnable { fp.add(FetchProfile.Item.BODY); localSrcFolder.fetch(messages, fp, null); localSrcFolder.copyMessages(messages, localDestFolder); + + if (unreadCountAffected) { + // If this copy operation changes the unread count in the destination + // folder, notify the listeners. + int unreadMessageCount = localDestFolder.getUnreadMessageCount(); + for (MessagingListener l : getListeners()) { + l.folderStatusChanged(account, destFolder, unreadMessageCount); + } + } } else { localSrcFolder.moveMessages(messages, localDestFolder); for (Map.Entry entry : origUidMap.entrySet()) { @@ -3333,6 +3347,17 @@ public class MessagingController implements Runnable { } unsuppressMessage(account, srcFolder, origUid); } + + if (unreadCountAffected) { + // If this move operation changes the unread count, notify the listeners + // that the unread count changed in both the source and destination folder. + int unreadMessageCountSrc = localSrcFolder.getUnreadMessageCount(); + int unreadMessageCountDest = localDestFolder.getUnreadMessageCount(); + for (MessagingListener l : getListeners()) { + l.folderStatusChanged(account, srcFolder, unreadMessageCountSrc); + l.folderStatusChanged(account, destFolder, unreadMessageCountDest); + } + } } queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidMap.keySet().toArray(EMPTY_STRING_ARRAY)); From 7a252bf002560fa9c00f79c962cc77d7021fca86 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 7 Feb 2012 16:32:13 +0100 Subject: [PATCH 12/22] Changed method to update the unread widget --- src/com/fsck/k9/K9.java | 20 ++++++++++++++++++++ src/com/fsck/k9/mail/store/LocalStore.java | 7 ------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index d48c79334..1da5b4d81 100644 --- a/src/com/fsck/k9/K9.java +++ b/src/com/fsck/k9/K9.java @@ -31,6 +31,7 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.BinaryTempFileBody; +import com.fsck.k9.provider.UnreadWidgetProvider; import com.fsck.k9.service.BootReceiver; import com.fsck.k9.service.MailService; import com.fsck.k9.service.ShutdownReceiver; @@ -504,19 +505,38 @@ public class K9 extends Application { } } + private void updateUnreadWidget() { + try { + UnreadWidgetProvider.updateUnreadCount(K9.this); + } catch (Exception e) { + if (K9.DEBUG) { + Log.e(LOG_TAG, "Error while updating unread widget(s)", e); + } + } + } + @Override public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) { broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message); + updateUnreadWidget(); } @Override public void messageDeleted(Account account, String folder, Message message) { broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message); + updateUnreadWidget(); } @Override public void synchronizeMailboxNewMessage(Account account, String folder, Message message) { broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_RECEIVED, account, folder, message); + updateUnreadWidget(); + } + + @Override + public void folderStatusChanged(Account account, String folderName, + int unreadMessageCount) { + updateUnreadWidget(); } @Override diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index ad8524090..049f4222e 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -60,7 +60,6 @@ import com.fsck.k9.mail.store.LockableDatabase.DbCallback; import com.fsck.k9.mail.store.LockableDatabase.WrappedException; import com.fsck.k9.mail.store.StorageManager.StorageProvider; import com.fsck.k9.provider.AttachmentProvider; -import com.fsck.k9.provider.UnreadWidgetProvider; /** *
@@ -500,9 +499,6 @@ public class LocalStore extends Store implements Serializable {
             public Void doDbWork(final SQLiteDatabase db) {
                 db.execSQL("DELETE FROM messages WHERE deleted = 0 and uid not like 'Local%'");
                 db.execSQL("update folders set flagged_count = 0, unread_count = 0");
-
-                // FIXME: hack to update unread count widget
-                UnreadWidgetProvider.updateUnreadCount(K9.app);
                 return null;
             }
         });
@@ -1315,9 +1311,6 @@ public class LocalStore extends Store implements Serializable {
         public void setUnreadMessageCount(final int unreadMessageCount) throws MessagingException {
             mUnreadMessageCount = Math.max(0, unreadMessageCount);
             updateFolderColumn("unread_count", mUnreadMessageCount);
-
-            // FIXME: hack to update unread count widget
-            UnreadWidgetProvider.updateUnreadCount(K9.app);
         }
 
         public void setFlaggedMessageCount(final int flaggedMessageCount) throws MessagingException {

From f36d2a6b231e013feb77d32d5732c0944785e3e7 Mon Sep 17 00:00:00 2001
From: cketti 
Date: Tue, 7 Feb 2012 19:38:46 +0100
Subject: [PATCH 13/22] Tweak widget layout

---
 res/layout/unread_widget_layout.xml | 18 ++++++++++--------
 res/xml/unread_widget_info.xml      |  4 ++--
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/res/layout/unread_widget_layout.xml b/res/layout/unread_widget_layout.xml
index 2b242b1bd..ce6684193 100644
--- a/res/layout/unread_widget_layout.xml
+++ b/res/layout/unread_widget_layout.xml
@@ -13,13 +13,14 @@
         android:layout_height="wrap_content">
 
         
 
         
+        android:textColor="#ffffff"
+        android:shadowColor="#000000"
+        android:shadowRadius="2.0"/>
 
 
diff --git a/res/xml/unread_widget_info.xml b/res/xml/unread_widget_info.xml
index 8e060337d..f2a7b27b0 100644
--- a/res/xml/unread_widget_info.xml
+++ b/res/xml/unread_widget_info.xml
@@ -1,8 +1,8 @@
 
 
 

From 64f4f7e4a3cd87e431d1beea85a22cb5962a2879 Mon Sep 17 00:00:00 2001
From: cketti 
Date: Tue, 7 Feb 2012 20:19:10 +0100
Subject: [PATCH 14/22] Use the new AccountList activity when creating launcher
 shortcuts

---
 src/com/fsck/k9/activity/AccountList.java     |  61 +++++---
 .../fsck/k9/activity/LauncherShortcuts.java   | 135 ++----------------
 .../activity/UnreadWidgetConfiguration.java   |  15 +-
 3 files changed, 71 insertions(+), 140 deletions(-)

diff --git a/src/com/fsck/k9/activity/AccountList.java b/src/com/fsck/k9/activity/AccountList.java
index 3e8ac0109..a229702ad 100644
--- a/src/com/fsck/k9/activity/AccountList.java
+++ b/src/com/fsck/k9/activity/AccountList.java
@@ -1,5 +1,9 @@
 package com.fsck.k9.activity;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.util.TypedValue;
@@ -12,17 +16,19 @@ import android.widget.ListView;
 import android.widget.TextView;
 
 import com.fsck.k9.Account;
+import com.fsck.k9.BaseAccount;
 import com.fsck.k9.FontSizes;
 import com.fsck.k9.K9;
 import com.fsck.k9.Preferences;
 import com.fsck.k9.R;
+import com.fsck.k9.SearchAccount;
 
 
 /**
  * Activity displaying the list of accounts.
  *
  * 

- * Classes extending this abstract class have to provide an {@link #onAccountSelected(Account)} + * Classes extending this abstract class have to provide an {@link #onAccountSelected(BaseAccount)} * method to perform an action when an account is selected. *

*/ @@ -51,47 +57,64 @@ public abstract class AccountList extends K9ListActivity implements OnItemClickL new LoadAccounts().execute(); } + @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - Account account = (Account) parent.getItemAtPosition(position); + BaseAccount account = (BaseAccount) parent.getItemAtPosition(position); onAccountSelected(account); } /** * Create a new {@link AccountsAdapter} instance and assign it to the {@link ListView}. * - * @param accounts + * @param realAccounts * An array of accounts to display. */ - public void populateListView(Account[] accounts) { + public void populateListView(Account[] realAccounts) { + List accounts = new ArrayList(); + + if (displaySpecialAccounts() && !K9.isHideSpecialAccounts()) { + BaseAccount integratedInboxAccount = new SearchAccount(this, true, null, null); + integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title)); + integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail)); + + BaseAccount unreadAccount = new SearchAccount(this, false, null, null); + unreadAccount.setDescription(getString(R.string.search_all_messages_title)); + unreadAccount.setEmail(getString(R.string.search_all_messages_detail)); + + accounts.add(integratedInboxAccount); + accounts.add(unreadAccount); + } + + accounts.addAll(Arrays.asList(realAccounts)); AccountsAdapter adapter = new AccountsAdapter(accounts); ListView listView = getListView(); listView.setAdapter(adapter); listView.invalidate(); } + /** + * Implementing decide whether or not to display special accounts in the list. + * + * @return {@code true}, if special accounts should be listed. {@code false}, otherwise. + */ + protected abstract boolean displaySpecialAccounts(); + /** * This method will be called when an account was selected. * * @param account * The account the user selected. */ - protected abstract void onAccountSelected(Account account); + protected abstract void onAccountSelected(BaseAccount account); - class AccountsAdapter extends ArrayAdapter { - private Account[] mAccounts; - - public AccountsAdapter(Account[] accounts) { + class AccountsAdapter extends ArrayAdapter { + public AccountsAdapter(List accounts) { super(AccountList.this, 0, accounts); - mAccounts = accounts; - } - - public Account[] getAccounts() { - return mAccounts; } @Override public View getView(int position, View convertView, ViewGroup parent) { - final Account account = getItem(position); + final BaseAccount account = getItem(position); final View view; if (convertView != null) { @@ -127,7 +150,13 @@ public abstract class AccountList extends K9ListActivity implements OnItemClickL holder.description.setText(description); - holder.chip.setBackgroundColor(account.getChipColor()); + if (account instanceof Account) { + Account realAccount = (Account) account; + holder.chip.setBackgroundColor(realAccount.getChipColor()); + } else { + holder.chip.setBackgroundColor(0xff999999); + } + holder.chip.getBackground().setAlpha(255); holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP, diff --git a/src/com/fsck/k9/activity/LauncherShortcuts.java b/src/com/fsck/k9/activity/LauncherShortcuts.java index aaf2008da..61bde33ca 100644 --- a/src/com/fsck/k9/activity/LauncherShortcuts.java +++ b/src/com/fsck/k9/activity/LauncherShortcuts.java @@ -1,90 +1,44 @@ package com.fsck.k9.activity; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import android.content.Intent; import android.os.Bundle; import android.os.Parcelable; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.TextView; import com.fsck.k9.Account; import com.fsck.k9.BaseAccount; -import com.fsck.k9.FontSizes; -import com.fsck.k9.K9; -import com.fsck.k9.Preferences; import com.fsck.k9.R; -import com.fsck.k9.SearchAccount; import com.fsck.k9.SearchSpecification; -import com.fsck.k9.helper.Utility; - -public class LauncherShortcuts extends K9ListActivity implements OnItemClickListener { - private AccountsAdapter mAdapter; - private FontSizes mFontSizes = K9.getFontSizes(); +public class LauncherShortcuts extends AccountList { @Override public void onCreate(Bundle icicle) { - super.onCreate(icicle); - // finish() immediately if we aren't supposed to be here if (!Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) { finish(); return; } - setContentView(R.layout.account_list); - ListView listView = getListView(); - listView.setOnItemClickListener(this); - listView.setItemsCanFocus(false); - - refresh(); + super.onCreate(icicle); } - private void refresh() { - List accounts = new ArrayList(); - - if (!K9.isHideSpecialAccounts()) { - BaseAccount integratedInboxAccount = new SearchAccount(this, true, null, null); - integratedInboxAccount.setDescription( - getString(R.string.integrated_inbox_title)); - integratedInboxAccount.setEmail( - getString(R.string.integrated_inbox_detail)); - - BaseAccount unreadAccount = new SearchAccount(this, false, null, null); - unreadAccount.setDescription( - getString(R.string.search_all_messages_title)); - unreadAccount.setEmail( - getString(R.string.search_all_messages_detail)); - - accounts.add(integratedInboxAccount); - accounts.add(unreadAccount); - } - - accounts.addAll(Arrays.asList(Preferences.getPreferences(this).getAccounts())); - - mAdapter = new AccountsAdapter(accounts); - getListView().setAdapter(mAdapter); + @Override + protected boolean displaySpecialAccounts() { + return true; } - private void setupShortcut(BaseAccount account) { + @Override + protected void onAccountSelected(BaseAccount account) { Intent shortcutIntent = null; if (account instanceof SearchSpecification) { - shortcutIntent = MessageList.actionHandleAccountIntent( - this, account.getDescription(), (SearchSpecification) account); + shortcutIntent = MessageList.actionHandleAccountIntent(this, account.getDescription(), + (SearchSpecification) account); } else { - shortcutIntent = FolderList.actionHandleAccountIntent(this, - (Account) account, null, true); + shortcutIntent = FolderList.actionHandleAccountIntent(this, (Account) account, null, + true); } + shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); String description = account.getDescription(); @@ -98,69 +52,4 @@ public class LauncherShortcuts extends K9ListActivity implements OnItemClickList setResult(RESULT_OK, intent); finish(); } - - public void onItemClick(AdapterView parent, View view, int position, long id) { - BaseAccount account = (BaseAccount) parent.getItemAtPosition(position); - setupShortcut(account); - } - - class AccountsAdapter extends ArrayAdapter { - public AccountsAdapter(List accounts) { - super(LauncherShortcuts.this, 0, accounts); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final BaseAccount account = getItem(position); - - final View view; - if (convertView != null) { - view = convertView; - } else { - view = getLayoutInflater().inflate(R.layout.accounts_item, parent, false); - view.findViewById(R.id.active_icons).setVisibility(View.GONE); - } - - AccountViewHolder holder = (AccountViewHolder) view.getTag(); - if (holder == null) { - holder = new AccountViewHolder(); - holder.description = (TextView) view.findViewById(R.id.description); - holder.email = (TextView) view.findViewById(R.id.email); - holder.chip = view.findViewById(R.id.chip); - - view.setTag(holder); - } - - String description = account.getDescription(); - if (account.getEmail().equals(description)) { - holder.email.setVisibility(View.GONE); - } else { - holder.email.setVisibility(View.VISIBLE); - holder.email.setText(account.getEmail()); - } - - if (description == null || description.length() == 0) { - description = account.getEmail(); - } - - holder.description.setText(description); - - if (account instanceof Account) { - holder.chip.setBackgroundColor(((Account) account).getChipColor()); - } - - holder.chip.getBackground().setAlpha(255); - - holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getAccountName()); - holder.email.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getAccountDescription()); - - return view; - } - - class AccountViewHolder { - public TextView description; - public TextView email; - public View chip; - } - } } diff --git a/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java b/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java index 482a51949..341f895ef 100644 --- a/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java +++ b/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java @@ -8,6 +8,7 @@ import android.content.SharedPreferences.Editor; import android.os.Bundle; import com.fsck.k9.Account; +import com.fsck.k9.BaseAccount; import com.fsck.k9.R; import com.fsck.k9.provider.UnreadWidgetProvider; @@ -55,7 +56,19 @@ public class UnreadWidgetConfiguration extends AccountList { } @Override - protected void onAccountSelected(Account account) { + protected boolean displaySpecialAccounts() { + return false; + } + + @Override + protected void onAccountSelected(BaseAccount baseAccount) { + if (!(baseAccount instanceof Account)) { + finish(); + return; + } + + Account account = (Account) baseAccount; + // Save widget configuration String accountUuid = account.getUuid(); saveAccountUuid(this, mAppWidgetId, accountUuid); From 485a505ca0aad9497b07a017e381a72cb639dbd3 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 9 Feb 2012 21:47:47 +0100 Subject: [PATCH 15/22] More tweaks to the unread widget --- res/drawable/unread_widget_background.xml | 15 +++++++++++++++ res/layout/unread_widget_layout.xml | 5 +++++ res/values/strings.xml | 2 +- .../fsck/k9/provider/UnreadWidgetProvider.java | 10 +++++++--- 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 res/drawable/unread_widget_background.xml diff --git a/res/drawable/unread_widget_background.xml b/res/drawable/unread_widget_background.xml new file mode 100644 index 000000000..52e295f83 --- /dev/null +++ b/res/drawable/unread_widget_background.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/res/layout/unread_widget_layout.xml b/res/layout/unread_widget_layout.xml index ce6684193..4ad2c399e 100644 --- a/res/layout/unread_widget_layout.xml +++ b/res/layout/unread_widget_layout.xml @@ -6,6 +6,9 @@ android:layout_height="wrap_content" android:padding="2dp" android:orientation="vertical" + android:clickable="true" + android:focusable="true" + android:background="@drawable/unread_widget_background" android:gravity="bottom|center_horizontal"> Move down Moving account... - Unread count + K-9 Unread Show unread count for… diff --git a/src/com/fsck/k9/provider/UnreadWidgetProvider.java b/src/com/fsck/k9/provider/UnreadWidgetProvider.java index 8913fb4e7..616707c34 100644 --- a/src/com/fsck/k9/provider/UnreadWidgetProvider.java +++ b/src/com/fsck/k9/provider/UnreadWidgetProvider.java @@ -20,6 +20,7 @@ import android.view.View; import android.widget.RemoteViews; public class UnreadWidgetProvider extends AppWidgetProvider { + private static final int MAX_COUNT = 9999; /** * Trigger update for all of our unread widgets. @@ -71,11 +72,15 @@ public class UnreadWidgetProvider extends AppWidgetProvider { } } - if (unreadCount == 0) { + if (unreadCount <= 0) { // Hide TextView for unread count if there are no unread messages. remoteViews.setViewVisibility(R.id.unread_count, View.GONE); } else { - remoteViews.setTextViewText(R.id.unread_count, String.valueOf(unreadCount)); + remoteViews.setViewVisibility(R.id.unread_count, View.VISIBLE); + + String displayCount = (unreadCount <= MAX_COUNT) ? + String.valueOf(unreadCount) : String.valueOf(MAX_COUNT) + "+"; + remoteViews.setTextViewText(R.id.unread_count, displayCount); } remoteViews.setTextViewText(R.id.account_name, accountName); @@ -95,7 +100,6 @@ public class UnreadWidgetProvider extends AppWidgetProvider { remoteViews.setOnClickPendingIntent(R.id.unread_widget_layout, pendingIntent); appWidgetManager.updateAppWidget(appWidgetId, remoteViews); - } From dfd47702bcc634892f4c4f63012010ca0de9568b Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 14 Feb 2012 00:27:28 +0100 Subject: [PATCH 16/22] Fixed rare NullPointerException --- src/com/fsck/k9/EmailAddressAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/fsck/k9/EmailAddressAdapter.java b/src/com/fsck/k9/EmailAddressAdapter.java index d9bb59940..8cb0240d9 100644 --- a/src/com/fsck/k9/EmailAddressAdapter.java +++ b/src/com/fsck/k9/EmailAddressAdapter.java @@ -48,7 +48,7 @@ public class EmailAddressAdapter extends ResourceCursorAdapter { final String name = mContacts.getName(cursor); final String address = mContacts.getEmail(cursor); - return new Address(address, name).toString(); + return (address == null) ? "" : new Address(address, name).toString(); } @Override From 81b4fef4aac0978459da79d752a18d57ecf3264e Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 14 Feb 2012 00:49:34 +0100 Subject: [PATCH 17/22] Change protection level for READ_MESSAGES and DELETE_MESSAGES to "dangerous" --- AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f530b902f..383ec77a3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -43,13 +43,13 @@ From 4c318d172719905ec4b98d0e56273eecd0d1670b Mon Sep 17 00:00:00 2001 From: wilian-cb Date: Tue, 14 Feb 2012 21:11:15 -0200 Subject: [PATCH 18/22] 3945: Treatment for errors related to not having a file manager application in Android to handle import configurations. --- res/values/strings.xml | 6 +++++ src/com/fsck/k9/activity/Accounts.java | 34 +++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 89e760667..79a9efa0e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1128,4 +1128,10 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin K-9 Unread Show unread count for… + + Missing File Manager Application + There is no suitable application to handle +the import operation. Please install a file manager application from Android Market + Open Market + Close diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index d49ee0be0..0aa7693c8 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -24,6 +24,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -106,6 +107,11 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC */ private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0]; + /** + * URL used to open Android Market application + */ + private static final String ANDROID_MARKET_URL = "http://market.android.com/"; + /** * Number of special accounts ('Unified Inbox' and 'All Messages') */ @@ -1256,7 +1262,33 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType(MimeUtility.K9_SETTINGS_MIME_TYPE); - startActivityForResult(Intent.createChooser(i, null), ACTIVITY_REQUEST_PICK_SETTINGS_FILE); + + PackageManager packageManager = getPackageManager(); + List infos = packageManager.queryIntentActivities(i, 0); + + if (infos.size() > 0) { + startActivityForResult(Intent.createChooser(i, null), + ACTIVITY_REQUEST_PICK_SETTINGS_FILE); + } else { + DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int button) { + if (button == DialogInterface.BUTTON_POSITIVE) { + Uri uri = Uri.parse(ANDROID_MARKET_URL); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + } else if (button == DialogInterface.BUTTON_NEGATIVE) { + dialog.dismiss(); + } + } + }; + + new AlertDialog.Builder(this) + .setTitle(getString(R.string.import_dialog_error_title)) + .setMessage(getString(R.string.import_dialog_error_message)) + .setPositiveButton(getString(R.string.open_market), listener) + .setNegativeButton(getString(R.string.close), listener) + .show(); + } } @Override From 15ffaf301eb626e3b2e69c0edf1fce51998835ec Mon Sep 17 00:00:00 2001 From: ashley willis Date: Tue, 14 Feb 2012 22:48:27 -0600 Subject: [PATCH 19/22] changed ANDROID_MARKET_URL to automatically search for OI File Manager. --- src/com/fsck/k9/activity/Accounts.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index 0aa7693c8..9ac2a5926 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -110,7 +110,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC /** * URL used to open Android Market application */ - private static final String ANDROID_MARKET_URL = "http://market.android.com/"; + private static final String ANDROID_MARKET_URL = "https://market.android.com/search?q=oi+file+manager&c=apps"; /** * Number of special accounts ('Unified Inbox' and 'All Messages') From ddbd46666a3b79ad6a33fdc20148c7d090d18097 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Wed, 15 Feb 2012 13:46:33 -0500 Subject: [PATCH 20/22] Bumped manifest to 4.109 --- AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 383ec77a3..cdfe9b30b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,8 +1,8 @@ Date: Thu, 16 Feb 2012 14:43:38 +0100 Subject: [PATCH 21/22] Use the Androiod framework for the file manager error dialog This will automatically recreate the dialog on configuration changes (e.g. orientation change). --- src/com/fsck/k9/activity/Accounts.java | 38 ++++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index 9ac2a5926..031785e62 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -120,6 +120,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC private static final int DIALOG_REMOVE_ACCOUNT = 1; private static final int DIALOG_CLEAR_ACCOUNT = 2; private static final int DIALOG_RECREATE_ACCOUNT = 3; + private static final int DIALOG_NO_FILE_MANAGER = 4; + private ConcurrentHashMap accountStats = new ConcurrentHashMap(); private ConcurrentHashMap pendingWork = new ConcurrentHashMap(); @@ -986,6 +988,20 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC } } }); + case DIALOG_NO_FILE_MANAGER: + return ConfirmationDialog.create(this, id, + R.string.import_dialog_error_title, + getString(R.string.import_dialog_error_message), + R.string.open_market, + R.string.close, + new Runnable() { + @Override + public void run() { + Uri uri = Uri.parse(ANDROID_MARKET_URL); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + } + }); } return super.onCreateDialog(id); } @@ -1007,6 +1023,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC alert.setMessage(getString(R.string.account_recreate_dlg_instructions_fmt, mSelectedContextAccount.getDescription())); break; + case DIALOG_NO_FILE_MANAGER: + alert.setMessage(getString(R.string.import_dialog_error_message)); + break; } super.onPrepareDialog(id, d); @@ -1270,24 +1289,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC startActivityForResult(Intent.createChooser(i, null), ACTIVITY_REQUEST_PICK_SETTINGS_FILE); } else { - DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int button) { - if (button == DialogInterface.BUTTON_POSITIVE) { - Uri uri = Uri.parse(ANDROID_MARKET_URL); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivity(intent); - } else if (button == DialogInterface.BUTTON_NEGATIVE) { - dialog.dismiss(); - } - } - }; - - new AlertDialog.Builder(this) - .setTitle(getString(R.string.import_dialog_error_title)) - .setMessage(getString(R.string.import_dialog_error_message)) - .setPositiveButton(getString(R.string.open_market), listener) - .setNegativeButton(getString(R.string.close), listener) - .show(); + showDialog(DIALOG_NO_FILE_MANAGER); } } From 53ae9d7fe77c5c1ab0c5e5bc1778f8bb061e71cc Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 16 Feb 2012 14:52:56 +0100 Subject: [PATCH 22/22] Avoid exception when a screen reader is installed but not active. --- src/com/fsck/k9/view/SingleMessageView.java | 22 ++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java index 48206f8ed..92699ee2c 100644 --- a/src/com/fsck/k9/view/SingleMessageView.java +++ b/src/com/fsck/k9/view/SingleMessageView.java @@ -102,15 +102,19 @@ public class SingleMessageView extends LinearLayout { // content://.providers.StatusProvider cursor = cr.query(Uri.parse("content://" + screenReader.serviceInfo.packageName + ".providers.StatusProvider"), null, null, null, null); - if (cursor != null) { - cursor.moveToFirst(); - // These content providers use a special cursor that only has - // one element, - // an integer that is 1 if the screen reader is running. - status = cursor.getInt(0); - cursor.close(); - if (status == 1) { - return true; + try { + if (cursor != null && cursor.moveToFirst()) { + // These content providers use a special cursor that only has + // one element, + // an integer that is 1 if the screen reader is running. + status = cursor.getInt(0); + if (status == 1) { + return true; + } + } + } finally { + if (cursor != null) { + cursor.close(); } } }