diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9b8501028..cdfe9b30b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,8 +1,8 @@ @@ -208,6 +208,10 @@ android:launchMode="singleTask" android:configChanges="locale" > + + + + + + + + + + + + + + + + + + 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/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/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..4ad2c399e --- /dev/null +++ b/res/layout/unread_widget_layout.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index a39cd7129..57be7aa88 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1145,4 +1145,12 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin Move down Moving account... + 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/res/xml/unread_widget_info.xml b/res/xml/unread_widget_info.xml new file mode 100644 index 000000000..f2a7b27b0 --- /dev/null +++ b/res/xml/unread_widget_info.xml @@ -0,0 +1,8 @@ + + + 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 diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index 0ba938233..1eb891a62 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; @@ -505,19 +506,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/activity/AccountList.java b/src/com/fsck/k9/activity/AccountList.java new file mode 100644 index 000000000..a229702ad --- /dev/null +++ b/src/com/fsck/k9/activity/AccountList.java @@ -0,0 +1,192 @@ +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; +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; + + +/** + * Activity displaying the list of accounts. + * + *

+ * Classes extending this abstract class have to provide an {@link #onAccountSelected(BaseAccount)} + * 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(); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + BaseAccount account = (BaseAccount) parent.getItemAtPosition(position); + onAccountSelected(account); + } + + /** + * Create a new {@link AccountsAdapter} instance and assign it to the {@link ListView}. + * + * @param realAccounts + * An array of accounts to display. + */ + 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(BaseAccount account); + + class AccountsAdapter extends ArrayAdapter { + public AccountsAdapter(List accounts) { + super(AccountList.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); + 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); + + 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, + 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/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index 51224e34d..4df049d20 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -26,6 +26,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; @@ -108,10 +109,22 @@ 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 = "https://market.android.com/search?q=oi+file+manager&c=apps"; + + /** + * 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; + private static final int DIALOG_NO_FILE_MANAGER = 4; private static final String TRUE = "true"; + private ConcurrentHashMap accountStats = new ConcurrentHashMap(); private ConcurrentHashMap pendingWork = new ConcurrentHashMap(); @@ -328,13 +341,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(); @@ -377,6 +384,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) { @@ -461,9 +481,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 { @@ -990,6 +1014,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); } @@ -1011,6 +1049,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); @@ -1266,7 +1307,16 @@ 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 { + showDialog(DIALOG_NO_FILE_MANAGER); + } } @Override diff --git a/src/com/fsck/k9/activity/LauncherShortcuts.java b/src/com/fsck/k9/activity/LauncherShortcuts.java index f6e01c1ab..61bde33ca 100644 --- a/src/com/fsck/k9/activity/LauncherShortcuts.java +++ b/src/com/fsck/k9/activity/LauncherShortcuts.java @@ -3,53 +3,42 @@ package com.fsck.k9.activity; 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.FontSizes; -import com.fsck.k9.K9; -import com.fsck.k9.Preferences; +import com.fsck.k9.BaseAccount; import com.fsck.k9.R; +import com.fsck.k9.SearchSpecification; -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.launcher_shortcuts); - ListView listView = getListView(); - listView.setOnItemClickListener(this); - listView.setItemsCanFocus(false); - - refresh(); + super.onCreate(icicle); } - private void refresh() { - Account[] accounts = Preferences.getPreferences(this).getAccounts(); - - mAdapter = new AccountsAdapter(accounts); - getListView().setAdapter(mAdapter); + @Override + protected boolean displaySpecialAccounts() { + return true; } - private void setupShortcut(Account account) { - final Intent shortcutIntent = FolderList.actionHandleAccountIntent(this, account, null, true); + @Override + protected void onAccountSelected(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); + } + + shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); String description = account.getDescription(); @@ -63,66 +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) { - Account account = (Account) parent.getItemAtPosition(position); - setupShortcut(account); - } - - class AccountsAdapter extends ArrayAdapter { - public AccountsAdapter(Account[] accounts) { - super(LauncherShortcuts.this, 0, accounts); - } - - @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); - } - - 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; - } - } } 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 81ca1630e..b3d2d9c2c 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; @@ -70,6 +72,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; @@ -613,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) { @@ -629,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); } @@ -1392,13 +1406,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 7f4256131..49af15217 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; @@ -715,17 +714,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); } } @@ -858,18 +848,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/activity/UnreadWidgetConfiguration.java b/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java new file mode 100644 index 000000000..341f895ef --- /dev/null +++ b/src/com/fsck/k9/activity/UnreadWidgetConfiguration.java @@ -0,0 +1,106 @@ +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.BaseAccount; +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 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); + + // 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/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index a34c095d2..9a4af81e4 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -2622,51 +2622,67 @@ 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. LocalFolder localFolder = null; try { LocalStore 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; } @@ -2675,16 +2691,54 @@ public class MessagingController implements Runnable { 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 uid + * The UID of 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); + if (message != null) { + 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 { @@ -3334,6 +3388,8 @@ public class MessagingController implements Runnable { Store remoteStore = account.getRemoteStore(); LocalFolder localSrcFolder = localStore.getFolder(srcFolder); LocalFolder localDestFolder = localStore.getFolder(destFolder); + + boolean unreadCountAffected = false; List uids = new LinkedList(); List localUids = new LinkedList(); @@ -3350,6 +3406,10 @@ public class MessagingController implements Runnable { needToLocalizeSourceFolder = true; } } + + if (!unreadCountAffected && !message.isSet(Flag.SEEN)) { + unreadCountAffected = true; + } } // make sure the remote folder actually gets created even though it won't be used yet. @@ -3417,6 +3477,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()) { @@ -3427,6 +3496,16 @@ 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); + } + } } if (!localDestFolder.isLocalOnly() && @@ -3480,6 +3559,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); + } + } } FetchProfile fp = new FetchProfile(); @@ -3495,6 +3585,16 @@ public class MessagingController implements Runnable { // local message copy to local folder localSrcFolder.copyMessages(localMessages, localDestFolder); } + + if (isCopy && unreadCountAffected) { + // ASH this did happen right after removed "localSrcFolder.copyMessages(messages, localDestFolder);" + // 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); + } + } } processPendingCommands(account); @@ -3752,7 +3852,8 @@ public class MessagingController implements Runnable { 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); diff --git a/src/com/fsck/k9/controller/MessagingListener.java b/src/com/fsck/k9/controller/MessagingListener.java index bdf35fa00..49ddbf4da 100644 --- a/src/com/fsck/k9/controller/MessagingListener.java +++ b/src/com/fsck/k9/controller/MessagingListener.java @@ -12,191 +12,154 @@ 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 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 messageDeleted(Account account, String folder, Message message) {} - public void loadAttachmentFinished( - Account account, - Message message, - Part part, - Object tag) { - } + public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {} - public void loadAttachmentFailed( - Account account, - Message message, - Part part, - Object tag, - String reason) { - } + + 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) {} } diff --git a/src/com/fsck/k9/provider/UnreadWidgetProvider.java b/src/com/fsck/k9/provider/UnreadWidgetProvider.java new file mode 100644 index 000000000..616707c34 --- /dev/null +++ b/src/com/fsck/k9/provider/UnreadWidgetProvider.java @@ -0,0 +1,127 @@ +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 { + private static final int MAX_COUNT = 9999; + + /** + * 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.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); + + 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); + } + } +} 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(); } } }