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();
}
}
}