Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 Google Inc. |
| 3 | * Licensed to The Android Open Source Project. |
| 4 | * |
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at |
| 8 | * |
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | */ |
| 17 | |
| 18 | package com.android.mail.browse; |
| 19 | |
Paul Westbrook | bf232c3 | 2012-04-18 03:17:41 -0700 | [diff] [blame] | 20 | import android.app.AlertDialog; |
Mindy Pereira | 6c2663d | 2012-07-20 15:37:29 -0700 | [diff] [blame] | 21 | import android.content.ContentValues; |
Paul Westbrook | bf232c3 | 2012-04-18 03:17:41 -0700 | [diff] [blame] | 22 | import android.content.Context; |
| 23 | import android.content.DialogInterface; |
Marc Blank | a6b671d | 2012-05-25 12:52:02 -0700 | [diff] [blame] | 24 | import android.net.Uri; |
Paul Westbrook | bf232c3 | 2012-04-18 03:17:41 -0700 | [diff] [blame] | 25 | import android.view.ActionMode; |
| 26 | import android.view.Menu; |
| 27 | import android.view.MenuInflater; |
| 28 | import android.view.MenuItem; |
Marc Blank | a6b671d | 2012-05-25 12:52:02 -0700 | [diff] [blame] | 29 | import android.widget.Toast; |
Paul Westbrook | bf232c3 | 2012-04-18 03:17:41 -0700 | [diff] [blame] | 30 | |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 31 | import com.android.mail.R; |
Mindy Pereira | 30d2d32 | 2012-02-03 08:40:49 -0800 | [diff] [blame] | 32 | import com.android.mail.providers.Account; |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 33 | import com.android.mail.providers.Conversation; |
Mindy Pereira | 6c2663d | 2012-07-20 15:37:29 -0700 | [diff] [blame] | 34 | import com.android.mail.providers.ConversationInfo; |
Mindy Pereira | 4f166de | 2012-02-14 13:40:58 -0800 | [diff] [blame] | 35 | import com.android.mail.providers.Folder; |
Marc Blank | a6b671d | 2012-05-25 12:52:02 -0700 | [diff] [blame] | 36 | import com.android.mail.providers.MailAppProvider; |
Mindy Pereira | 6c2663d | 2012-07-20 15:37:29 -0700 | [diff] [blame] | 37 | import com.android.mail.providers.MessageInfo; |
Mindy Pereira | efe3d25 | 2012-03-01 14:20:44 -0800 | [diff] [blame] | 38 | import com.android.mail.providers.Settings; |
Mindy Pereira | f98b318 | 2012-02-22 11:07:13 -0800 | [diff] [blame] | 39 | import com.android.mail.providers.UIProvider; |
Marc Blank | cf164d6 | 2012-04-20 08:56:17 -0700 | [diff] [blame] | 40 | import com.android.mail.providers.UIProvider.ConversationColumns; |
Paul Westbrook | bf232c3 | 2012-04-18 03:17:41 -0700 | [diff] [blame] | 41 | import com.android.mail.providers.UIProvider.FolderCapabilities; |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 42 | import com.android.mail.ui.ControllableActivity; |
Vikram Aggarwal | 1ddcf0f | 2012-01-13 11:45:02 -0800 | [diff] [blame] | 43 | import com.android.mail.ui.ConversationSelectionSet; |
| 44 | import com.android.mail.ui.ConversationSetObserver; |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 45 | import com.android.mail.ui.ConversationUpdater; |
Marc Blank | a6b671d | 2012-05-25 12:52:02 -0700 | [diff] [blame] | 46 | import com.android.mail.ui.DestructiveAction; |
Mindy Pereira | f3dfb15 | 2012-02-06 12:55:13 -0800 | [diff] [blame] | 47 | import com.android.mail.ui.FoldersSelectionDialog; |
Paul Westbrook | bf232c3 | 2012-04-18 03:17:41 -0700 | [diff] [blame] | 48 | import com.android.mail.ui.RestrictedActivity; |
Mindy Pereira | 07118a0 | 2012-04-02 16:35:01 -0700 | [diff] [blame] | 49 | import com.android.mail.ui.SwipeableListView; |
Paul Westbrook | b334c90 | 2012-06-25 11:42:46 -0700 | [diff] [blame] | 50 | import com.android.mail.utils.LogTag; |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 51 | import com.android.mail.utils.LogUtils; |
Mindy Pereira | efe3d25 | 2012-03-01 14:20:44 -0800 | [diff] [blame] | 52 | import com.android.mail.utils.Utils; |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 53 | import com.google.common.annotations.VisibleForTesting; |
| 54 | |
Mindy Pereira | 6c2663d | 2012-07-20 15:37:29 -0700 | [diff] [blame] | 55 | import org.json.JSONException; |
| 56 | |
Mindy Pereira | d92e9f3 | 2012-02-03 09:10:58 -0800 | [diff] [blame] | 57 | import java.util.ArrayList; |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 58 | import java.util.Collection; |
| 59 | |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 60 | /** |
| 61 | * A component that displays a custom view for an {@code ActionBar}'s {@code |
| 62 | * ContextMode} specific to operating on a set of conversations. |
| 63 | */ |
| 64 | public class SelectedConversationsActionMenu implements ActionMode.Callback, |
Vikram Aggarwal | d503df4 | 2012-05-11 10:13:35 -0700 | [diff] [blame] | 65 | ConversationSetObserver { |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 66 | |
Paul Westbrook | b334c90 | 2012-06-25 11:42:46 -0700 | [diff] [blame] | 67 | private static final String LOG_TAG = LogTag.getLogTag(); |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 68 | |
| 69 | /** |
| 70 | * The set of conversations to display the menu for. |
| 71 | */ |
| 72 | protected final ConversationSelectionSet mSelectionSet; |
| 73 | |
Vikram Aggarwal | d247dc9 | 2012-02-10 15:49:01 -0800 | [diff] [blame] | 74 | private final RestrictedActivity mActivity; |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 75 | |
Vikram Aggarwal | cd8bf0a | 2012-02-14 13:13:18 -0800 | [diff] [blame] | 76 | /** |
| 77 | * Context of the activity. A dialog requires the context of an activity rather than the global |
| 78 | * root context of the process. So mContext = mActivity.getApplicationContext() will fail. |
| 79 | */ |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 80 | private final Context mContext; |
| 81 | |
| 82 | @VisibleForTesting |
Vikram Aggarwal | 4f4782b | 2012-05-30 08:39:09 -0700 | [diff] [blame] | 83 | private ActionMode mActionMode; |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 84 | |
| 85 | private boolean mActivated = false; |
| 86 | |
| 87 | private Menu mMenu; |
| 88 | |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 89 | /** Object that can update conversation state on our behalf. */ |
| 90 | private final ConversationUpdater mUpdater; |
Mindy Pereira | 30d2d32 | 2012-02-03 08:40:49 -0800 | [diff] [blame] | 91 | |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 92 | private final Account mAccount; |
Mindy Pereira | d92e9f3 | 2012-02-03 09:10:58 -0800 | [diff] [blame] | 93 | |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 94 | private final Folder mFolder; |
Mindy Pereira | 4f166de | 2012-02-14 13:40:58 -0800 | [diff] [blame] | 95 | |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 96 | private final SwipeableListView mListView; |
Mindy Pereira | 07118a0 | 2012-04-02 16:35:01 -0700 | [diff] [blame] | 97 | |
Vikram Aggarwal | d247dc9 | 2012-02-10 15:49:01 -0800 | [diff] [blame] | 98 | public SelectedConversationsActionMenu(RestrictedActivity activity, |
Vikram Aggarwal | 4f4782b | 2012-05-30 08:39:09 -0700 | [diff] [blame] | 99 | ConversationSelectionSet selectionSet, Account account, |
Mindy Pereira | 07118a0 | 2012-04-02 16:35:01 -0700 | [diff] [blame] | 100 | Folder folder, SwipeableListView list) { |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 101 | mActivity = activity; |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 102 | mSelectionSet = selectionSet; |
Mindy Pereira | 30d2d32 | 2012-02-03 08:40:49 -0800 | [diff] [blame] | 103 | mAccount = account; |
Mindy Pereira | 4f166de | 2012-02-14 13:40:58 -0800 | [diff] [blame] | 104 | mFolder = folder; |
Mindy Pereira | 07118a0 | 2012-04-02 16:35:01 -0700 | [diff] [blame] | 105 | mListView = list; |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 106 | |
| 107 | mContext = mActivity.getActivityContext(); |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 108 | mUpdater = ((ControllableActivity) mActivity).getConversationUpdater(); |
Vikram Aggarwal | e8a8532 | 2012-04-24 09:01:38 -0700 | [diff] [blame] | 109 | } |
| 110 | |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 111 | @Override |
| 112 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
| 113 | boolean handled = true; |
Mindy Pereira | 8937bf1 | 2012-07-23 14:05:02 -0700 | [diff] [blame^] | 114 | // If the user taps a new menu item, commit any existing destructive actions. |
| 115 | mListView.commitDestructiveActions(); |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 116 | switch (item.getItemId()) { |
| 117 | case R.id.delete: |
Vikram Aggarwal | 7f602f7 | 2012-04-30 16:04:06 -0700 | [diff] [blame] | 118 | performDestructiveAction(R.id.delete); |
Marc Blank | 8d69d4e | 2012-01-25 12:04:28 -0800 | [diff] [blame] | 119 | break; |
Mindy Pereira | f98b318 | 2012-02-22 11:07:13 -0800 | [diff] [blame] | 120 | case R.id.archive: |
Vikram Aggarwal | 7f602f7 | 2012-04-30 16:04:06 -0700 | [diff] [blame] | 121 | performDestructiveAction(R.id.archive); |
Mindy Pereira | f98b318 | 2012-02-22 11:07:13 -0800 | [diff] [blame] | 122 | break; |
Mindy Pereira | 830c00f | 2012-02-22 11:43:49 -0800 | [diff] [blame] | 123 | case R.id.mute: |
Vikram Aggarwal | 0e091c5 | 2012-05-30 15:07:15 -0700 | [diff] [blame] | 124 | mUpdater.delete(mSelectionSet.values(), mUpdater.getBatchAction(R.id.mute)); |
Mindy Pereira | 830c00f | 2012-02-22 11:43:49 -0800 | [diff] [blame] | 125 | break; |
| 126 | case R.id.report_spam: |
Vikram Aggarwal | 0e091c5 | 2012-05-30 15:07:15 -0700 | [diff] [blame] | 127 | mUpdater.delete(mSelectionSet.values(), mUpdater.getBatchAction(R.id.report_spam)); |
Mindy Pereira | 830c00f | 2012-02-22 11:43:49 -0800 | [diff] [blame] | 128 | break; |
Paul Westbrook | 77eee62 | 2012-07-10 13:41:57 -0700 | [diff] [blame] | 129 | case R.id.mark_not_spam: |
| 130 | // Currently, since spam messages are only shown in list with other spam messages, |
| 131 | // marking a message not as spam is a destructive action |
| 132 | mUpdater.delete(mSelectionSet.values(), |
| 133 | mUpdater.getBatchAction(R.id.mark_not_spam)); |
| 134 | break; |
Paul Westbrook | 76b2062 | 2012-07-12 11:45:43 -0700 | [diff] [blame] | 135 | case R.id.report_phishing: |
| 136 | mUpdater.delete(mSelectionSet.values(), |
| 137 | mUpdater.getBatchAction(R.id.report_phishing)); |
| 138 | break; |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 139 | case R.id.read: |
| 140 | markConversationsRead(true); |
| 141 | break; |
| 142 | case R.id.unread: |
| 143 | markConversationsRead(false); |
Marc Blank | 8d69d4e | 2012-01-25 12:04:28 -0800 | [diff] [blame] | 144 | break; |
| 145 | case R.id.star: |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 146 | starConversations(true); |
| 147 | break; |
| 148 | case R.id.remove_star: |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 149 | if (mFolder.type == UIProvider.FolderType.STARRED) { |
| 150 | LogUtils.d(LOG_TAG, "We are in a starred folder, removing the star"); |
Vikram Aggarwal | 7f602f7 | 2012-04-30 16:04:06 -0700 | [diff] [blame] | 151 | performDestructiveAction(R.id.remove_star); |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 152 | } else { |
| 153 | LogUtils.d(LOG_TAG, "Not in a starred folder."); |
| 154 | starConversations(false); |
| 155 | } |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 156 | break; |
Mindy Pereira | 30d2d32 | 2012-02-03 08:40:49 -0800 | [diff] [blame] | 157 | case R.id.change_folder: |
Marc Blank | a6b671d | 2012-05-25 12:52:02 -0700 | [diff] [blame] | 158 | boolean cantMove = false; |
| 159 | Account acct = mAccount; |
| 160 | // Special handling for virtual folders |
| 161 | if (mFolder.supportsCapability(FolderCapabilities.IS_VIRTUAL)) { |
| 162 | Uri accountUri = null; |
| 163 | for (Conversation conv: mSelectionSet.values()) { |
| 164 | if (accountUri == null) { |
| 165 | accountUri = conv.accountUri; |
| 166 | } else if (!accountUri.equals(conv.accountUri)) { |
| 167 | // Tell the user why we can't do this |
| 168 | Toast.makeText(mContext, R.string.cant_move_or_change_labels, |
| 169 | Toast.LENGTH_LONG).show(); |
| 170 | cantMove = true; |
| 171 | break; |
| 172 | } |
| 173 | } |
| 174 | if (!cantMove) { |
| 175 | // Get the actual account here, so that we display its folders in the dialog |
| 176 | acct = MailAppProvider.getAccountFromAccountUri(accountUri); |
| 177 | } |
| 178 | } |
| 179 | if (!cantMove) { |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 180 | new FoldersSelectionDialog(mContext, acct, mUpdater, |
Marc Blank | a6b671d | 2012-05-25 12:52:02 -0700 | [diff] [blame] | 181 | mSelectionSet.values(), true).show(); |
| 182 | } |
Mindy Pereira | 30d2d32 | 2012-02-03 08:40:49 -0800 | [diff] [blame] | 183 | break; |
Mindy Pereira | 7f0a962 | 2012-02-29 15:00:34 -0800 | [diff] [blame] | 184 | case R.id.mark_important: |
| 185 | markConversationsImportant(true); |
| 186 | break; |
| 187 | case R.id.mark_not_important: |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 188 | if (mFolder.supportsCapability(UIProvider.FolderCapabilities.ONLY_IMPORTANT)) { |
Vikram Aggarwal | 7f602f7 | 2012-04-30 16:04:06 -0700 | [diff] [blame] | 189 | performDestructiveAction(R.id.mark_not_important); |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 190 | } else { |
| 191 | markConversationsImportant(false); |
| 192 | } |
Mindy Pereira | 7f0a962 | 2012-02-29 15:00:34 -0800 | [diff] [blame] | 193 | break; |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 194 | default: |
| 195 | handled = false; |
| 196 | break; |
| 197 | } |
| 198 | return handled; |
| 199 | } |
| 200 | |
Vikram Aggarwal | ad6d628 | 2012-03-28 09:16:14 -0700 | [diff] [blame] | 201 | /** |
| 202 | * Clear the selection and perform related UI changes to keep the state consistent. |
| 203 | */ |
| 204 | private void clearSelection() { |
| 205 | mSelectionSet.clear(); |
Vikram Aggarwal | ad6d628 | 2012-03-28 09:16:14 -0700 | [diff] [blame] | 206 | } |
| 207 | |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 208 | /** |
| 209 | * Update the underlying list adapter and redraw the menus if necessary. |
| 210 | */ |
| 211 | private void updateSelection() { |
Vikram Aggarwal | 4f4782b | 2012-05-30 08:39:09 -0700 | [diff] [blame] | 212 | mUpdater.refreshConversationList(); |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 213 | if (mActionMode != null) { |
| 214 | // Calling mActivity.invalidateOptionsMenu doesn't have the correct behavior, since |
| 215 | // the action mode is not refreshed when activity's options menu is invalidated. |
| 216 | // Since we need to refresh our own menu, it is easy to call onPrepareActionMode |
| 217 | // directly. |
| 218 | onPrepareActionMode(mActionMode, mActionMode.getMenu()); |
| 219 | } |
| 220 | } |
| 221 | |
Vikram Aggarwal | 7f602f7 | 2012-04-30 16:04:06 -0700 | [diff] [blame] | 222 | private void performDestructiveAction(final int id) { |
Vikram Aggarwal | 531488e | 2012-05-29 16:36:52 -0700 | [diff] [blame] | 223 | final DestructiveAction action = mUpdater.getBatchAction(id); |
Vikram Aggarwal | 7d81600 | 2012-04-17 17:06:41 -0700 | [diff] [blame] | 224 | final Settings settings = mActivity.getSettings(); |
Mindy Pereira | efe3d25 | 2012-03-01 14:20:44 -0800 | [diff] [blame] | 225 | final Collection<Conversation> conversations = mSelectionSet.values(); |
Vikram Aggarwal | 7d81600 | 2012-04-17 17:06:41 -0700 | [diff] [blame] | 226 | final boolean showDialog = (settings != null |
| 227 | && (id == R.id.delete) ? settings.confirmDelete : settings.confirmArchive); |
Mindy Pereira | efe3d25 | 2012-03-01 14:20:44 -0800 | [diff] [blame] | 228 | if (showDialog) { |
| 229 | int resId = id == R.id.delete ? R.plurals.confirm_delete_conversation |
| 230 | : R.plurals.confirm_archive_conversation; |
| 231 | CharSequence message = Utils.formatPlural(mContext, resId, conversations.size()); |
| 232 | new AlertDialog.Builder(mContext).setMessage(message) |
| 233 | .setPositiveButton(R.string.ok, new AlertDialog.OnClickListener() { |
| 234 | @Override |
| 235 | public void onClick(DialogInterface dialog, int which) { |
Vikram Aggarwal | 09a4bfe | 2012-04-23 13:07:50 -0700 | [diff] [blame] | 236 | destroy(id, conversations, action); |
Mindy Pereira | efe3d25 | 2012-03-01 14:20:44 -0800 | [diff] [blame] | 237 | } |
Mindy Pereira | efe3d25 | 2012-03-01 14:20:44 -0800 | [diff] [blame] | 238 | }).setNegativeButton(R.string.cancel, null).create().show(); |
| 239 | } else { |
Vikram Aggarwal | 09a4bfe | 2012-04-23 13:07:50 -0700 | [diff] [blame] | 240 | destroy(id, conversations, action); |
Mindy Pereira | 07118a0 | 2012-04-02 16:35:01 -0700 | [diff] [blame] | 241 | } |
| 242 | } |
| 243 | |
Vikram Aggarwal | 7f602f7 | 2012-04-30 16:04:06 -0700 | [diff] [blame] | 244 | private void destroy(int id, final Collection<Conversation> conversations, |
| 245 | final DestructiveAction listener) { |
Mindy Pereira | 07118a0 | 2012-04-02 16:35:01 -0700 | [diff] [blame] | 246 | if (id == R.id.archive) { |
| 247 | ArrayList<ConversationItemView> views = new ArrayList<ConversationItemView>(); |
| 248 | for (ConversationItemView view : mSelectionSet.views()) { |
| 249 | views.add(view); |
| 250 | } |
| 251 | mListView.archiveItems(views, listener); |
| 252 | } else { |
Vikram Aggarwal | 4f4782b | 2012-05-30 08:39:09 -0700 | [diff] [blame] | 253 | mUpdater.delete(conversations, listener); |
Mindy Pereira | efe3d25 | 2012-03-01 14:20:44 -0800 | [diff] [blame] | 254 | } |
| 255 | } |
| 256 | |
Vikram Aggarwal | 0e091c5 | 2012-05-30 15:07:15 -0700 | [diff] [blame] | 257 | /** |
| 258 | * Marks the read state of currently selected conversations (<b>and</b> the backing storage) |
| 259 | * to the value provided here. |
| 260 | * @param read is true if the conversations are to be marked as read, false if they are to be |
| 261 | * marked unread. |
| 262 | */ |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 263 | private void markConversationsRead(boolean read) { |
Mindy Pereira | 6c2663d | 2012-07-20 15:37:29 -0700 | [diff] [blame] | 264 | final Collection<Conversation> targets = mSelectionSet.values(); |
| 265 | ContentValues values; |
| 266 | ConversationInfo info; |
| 267 | for (Conversation target : targets) { |
| 268 | values = new ContentValues(); |
| 269 | values.put(ConversationColumns.READ, read); |
| 270 | info = target.conversationInfo; |
| 271 | if (info != null) { |
| 272 | try { |
| 273 | info.markRead(read); |
| 274 | values.put(ConversationColumns.CONVERSATION_INFO, |
| 275 | ConversationInfo.toString(info)); |
| 276 | } catch (JSONException e) { |
| 277 | LogUtils.e(LOG_TAG, e, "Error updating conversation info"); |
| 278 | } |
| 279 | } |
| 280 | mUpdater.updateConversation(Conversation.listOf(target), values); |
| 281 | } |
Vikram Aggarwal | 0e091c5 | 2012-05-30 15:07:15 -0700 | [diff] [blame] | 282 | // Update the conversations in the selection too. |
Mindy Pereira | 6c2663d | 2012-07-20 15:37:29 -0700 | [diff] [blame] | 283 | for (final Conversation c : targets) { |
Vikram Aggarwal | 0e091c5 | 2012-05-30 15:07:15 -0700 | [diff] [blame] | 284 | c.read = read; |
| 285 | } |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 286 | updateSelection(); |
Mindy Pereira | 7f0a962 | 2012-02-29 15:00:34 -0800 | [diff] [blame] | 287 | } |
| 288 | |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 289 | /** |
Vikram Aggarwal | 0e091c5 | 2012-05-30 15:07:15 -0700 | [diff] [blame] | 290 | * Marks the important state of currently selected conversations (<b>and</b> the backing |
| 291 | * storage) to the value provided here. |
| 292 | * @param important is true if the conversations are to be marked as important, false if they |
| 293 | * are to be marked not important. |
| 294 | */ |
| 295 | private void markConversationsImportant(boolean important) { |
| 296 | final Collection<Conversation> target = mSelectionSet.values(); |
| 297 | final int priority = important ? UIProvider.ConversationPriority.HIGH |
| 298 | : UIProvider.ConversationPriority.LOW; |
| 299 | mUpdater.updateConversation(target, ConversationColumns.PRIORITY, priority); |
| 300 | // Update the conversations in the selection too. |
| 301 | for (final Conversation c : target) { |
| 302 | c.priority = priority; |
| 303 | } |
| 304 | updateSelection(); |
| 305 | } |
| 306 | |
| 307 | /** |
| 308 | * Marks the selected conversations with the star setting provided here. |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 309 | * @param star true if you want all the conversations to have stars, false if you want to remove |
| 310 | * stars from all conversations |
| 311 | */ |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 312 | private void starConversations(boolean star) { |
Vikram Aggarwal | 0e091c5 | 2012-05-30 15:07:15 -0700 | [diff] [blame] | 313 | final Collection<Conversation> target = mSelectionSet.values(); |
| 314 | mUpdater.updateConversation(target, ConversationColumns.STARRED, star); |
| 315 | // Update the conversations in the selection too. |
| 316 | for (final Conversation c : target) { |
| 317 | c.starred = star; |
| 318 | } |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 319 | updateSelection(); |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 320 | } |
| 321 | |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 322 | @Override |
| 323 | public boolean onCreateActionMode(ActionMode mode, Menu menu) { |
| 324 | mSelectionSet.addObserver(this); |
Vikram Aggarwal | 7f602f7 | 2012-04-30 16:04:06 -0700 | [diff] [blame] | 325 | final MenuInflater inflater = mActivity.getMenuInflater(); |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 326 | inflater.inflate(R.menu.conversation_list_selection_actions_menu, menu); |
| 327 | mActionMode = mode; |
| 328 | mMenu = menu; |
| 329 | updateCount(); |
| 330 | return true; |
| 331 | } |
| 332 | |
| 333 | @Override |
| 334 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 335 | // Determine read/ unread |
| 336 | // Star/ unstar |
Vikram Aggarwal | 04dc819 | 2012-04-09 13:07:19 -0700 | [diff] [blame] | 337 | final Collection<Conversation> conversations = mSelectionSet.values(); |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 338 | boolean showStar = false; |
| 339 | boolean showMarkUnread = false; |
Mindy Pereira | 7f0a962 | 2012-02-29 15:00:34 -0800 | [diff] [blame] | 340 | boolean showMarkImportant = false; |
Paul Westbrook | 77eee62 | 2012-07-10 13:41:57 -0700 | [diff] [blame] | 341 | boolean showMarkNotSpam = false; |
Paul Westbrook | 76b2062 | 2012-07-12 11:45:43 -0700 | [diff] [blame] | 342 | boolean showMarkAsPhishing = false; |
Mindy Pereira | 7f0a962 | 2012-02-29 15:00:34 -0800 | [diff] [blame] | 343 | |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 344 | for (Conversation conversation : conversations) { |
| 345 | if (!conversation.starred) { |
| 346 | showStar = true; |
| 347 | } |
| 348 | if (conversation.read) { |
| 349 | showMarkUnread = true; |
| 350 | } |
Mindy Pereira | 7f0a962 | 2012-02-29 15:00:34 -0800 | [diff] [blame] | 351 | if (!conversation.isImportant()) { |
| 352 | showMarkImportant = true; |
| 353 | } |
Paul Westbrook | 77eee62 | 2012-07-10 13:41:57 -0700 | [diff] [blame] | 354 | if (conversation.spam) { |
| 355 | showMarkNotSpam = true; |
| 356 | } |
Paul Westbrook | 76b2062 | 2012-07-12 11:45:43 -0700 | [diff] [blame] | 357 | if (!conversation.phishing) { |
| 358 | showMarkAsPhishing = true; |
| 359 | } |
| 360 | if (showStar && showMarkUnread && showMarkImportant && showMarkNotSpam && |
| 361 | showMarkAsPhishing) { |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 362 | break; |
| 363 | } |
| 364 | } |
Paul Westbrook | 334e64a | 2012-02-23 13:26:35 -0800 | [diff] [blame] | 365 | final MenuItem star = menu.findItem(R.id.star); |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 366 | star.setVisible(showStar); |
Paul Westbrook | 334e64a | 2012-02-23 13:26:35 -0800 | [diff] [blame] | 367 | final MenuItem unstar = menu.findItem(R.id.remove_star); |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 368 | unstar.setVisible(!showStar); |
Paul Westbrook | 334e64a | 2012-02-23 13:26:35 -0800 | [diff] [blame] | 369 | final MenuItem read = menu.findItem(R.id.read); |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 370 | read.setVisible(!showMarkUnread); |
Paul Westbrook | 334e64a | 2012-02-23 13:26:35 -0800 | [diff] [blame] | 371 | final MenuItem unread = menu.findItem(R.id.unread); |
Mindy Pereira | febeb5f | 2012-02-14 15:08:13 -0800 | [diff] [blame] | 372 | unread.setVisible(showMarkUnread); |
Paul Westbrook | 334e64a | 2012-02-23 13:26:35 -0800 | [diff] [blame] | 373 | final MenuItem archive = menu.findItem(R.id.archive); |
| 374 | archive.setVisible(mAccount.supportsCapability(UIProvider.AccountCapabilities.ARCHIVE) && |
| 375 | mFolder.supportsCapability(FolderCapabilities.ARCHIVE)); |
| 376 | final MenuItem spam = menu.findItem(R.id.report_spam); |
| 377 | spam.setVisible(mAccount.supportsCapability(UIProvider.AccountCapabilities.REPORT_SPAM) && |
Paul Westbrook | 77eee62 | 2012-07-10 13:41:57 -0700 | [diff] [blame] | 378 | mFolder.supportsCapability(FolderCapabilities.REPORT_SPAM)); |
| 379 | final MenuItem notSpam = menu.findItem(R.id.mark_not_spam); |
| 380 | notSpam.setVisible(showMarkNotSpam && |
| 381 | mAccount.supportsCapability(UIProvider.AccountCapabilities.REPORT_SPAM) && |
| 382 | mFolder.supportsCapability(FolderCapabilities.MARK_NOT_SPAM)); |
Paul Westbrook | 76b2062 | 2012-07-12 11:45:43 -0700 | [diff] [blame] | 383 | final MenuItem phishing = menu.findItem(R.id.report_phishing); |
| 384 | phishing.setVisible(showMarkAsPhishing && |
| 385 | mAccount.supportsCapability(UIProvider.AccountCapabilities.REPORT_PHISHING) && |
| 386 | mFolder.supportsCapability(FolderCapabilities.REPORT_PHISHING)); |
| 387 | |
Paul Westbrook | 334e64a | 2012-02-23 13:26:35 -0800 | [diff] [blame] | 388 | final MenuItem mute = menu.findItem(R.id.mute); |
Mindy Pereira | 830c00f | 2012-02-22 11:43:49 -0800 | [diff] [blame] | 389 | mute.setVisible(mAccount.supportsCapability(UIProvider.AccountCapabilities.MUTE)); |
Mindy Pereira | 7f0a962 | 2012-02-29 15:00:34 -0800 | [diff] [blame] | 390 | final MenuItem markImportant = menu.findItem(R.id.mark_important); |
| 391 | markImportant.setVisible(showMarkImportant |
| 392 | && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT)); |
| 393 | final MenuItem markNotImportant = menu.findItem(R.id.mark_not_important); |
| 394 | markNotImportant.setVisible(!showMarkImportant |
| 395 | && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT)); |
| 396 | |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 397 | return true; |
| 398 | } |
| 399 | |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 400 | @Override |
| 401 | public void onDestroyActionMode(ActionMode mode) { |
| 402 | mActionMode = null; |
| 403 | // The action mode may have been destroyed due to this menu being deactivated, in which |
| 404 | // case resources need not be cleaned up. However, if it was destroyed while this menu is |
| 405 | // active, that implies the user hit "Done" in the top right, and resources need cleaning. |
| 406 | if (mActivated) { |
| 407 | destroy(); |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 408 | } |
| 409 | mMenu = null; |
| 410 | } |
| 411 | |
| 412 | @Override |
Vikram Aggarwal | 1ddcf0f | 2012-01-13 11:45:02 -0800 | [diff] [blame] | 413 | public void onSetPopulated(ConversationSelectionSet set) { |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 414 | // Noop. This object can only exist while the set is non-empty. |
| 415 | } |
| 416 | |
| 417 | @Override |
Vikram Aggarwal | 1ddcf0f | 2012-01-13 11:45:02 -0800 | [diff] [blame] | 418 | public void onSetEmpty() { |
Vikram Aggarwal | ad6d628 | 2012-03-28 09:16:14 -0700 | [diff] [blame] | 419 | LogUtils.d(LOG_TAG, "onSetEmpty called."); |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 420 | destroy(); |
| 421 | } |
| 422 | |
| 423 | @Override |
| 424 | public void onSetChanged(ConversationSelectionSet set) { |
| 425 | // If the set is empty, the menu buttons are invalid and most like the menu will be cleaned |
| 426 | // up. Avoid making any changes to stop flickering ("Add Star" -> "Remove Star") just |
| 427 | // before hiding the menu. |
| 428 | if (set.isEmpty()) { |
| 429 | return; |
| 430 | } |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 431 | updateCount(); |
| 432 | } |
| 433 | |
| 434 | /** |
| 435 | * Updates the visible count of how many conversations are selected. |
| 436 | */ |
| 437 | private void updateCount() { |
| 438 | if (mActionMode != null) { |
| 439 | mActionMode.setTitle(mContext.getString(R.string.num_selected, mSelectionSet.size())); |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | /** |
Vikram Aggarwal | e128fc2 | 2012-04-04 12:33:34 -0700 | [diff] [blame] | 444 | * Activates and shows this menu (essentially starting an {@link ActionMode}) if the selected |
| 445 | * set is non-empty. |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 446 | */ |
| 447 | public void activate() { |
Vikram Aggarwal | e128fc2 | 2012-04-04 12:33:34 -0700 | [diff] [blame] | 448 | if (mSelectionSet.isEmpty()) { |
| 449 | // We have nothing to do since there is no conversation selected. |
| 450 | return; |
| 451 | } |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 452 | mActivated = true; |
| 453 | if (mActionMode == null) { |
| 454 | mActivity.startActionMode(this); |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | /** |
| 459 | * De-activates and hides the menu (essentially disabling the {@link ActionMode}), but maintains |
| 460 | * the selection conversation set, and internally updates state as necessary. |
| 461 | */ |
| 462 | public void deactivate() { |
| 463 | if (mActionMode != null) { |
| 464 | mActivated = false; |
| 465 | mActionMode.finish(); |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | @VisibleForTesting |
| 470 | public boolean isActivated() { |
| 471 | return mActivated; |
| 472 | } |
| 473 | |
| 474 | /** |
| 475 | * Destroys and cleans up the resources associated with this menu. |
| 476 | */ |
Vikram Aggarwal | ad6d628 | 2012-03-28 09:16:14 -0700 | [diff] [blame] | 477 | private void destroy() { |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 478 | deactivate(); |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 479 | mSelectionSet.removeObserver(this); |
Vikram Aggarwal | ad6d628 | 2012-03-28 09:16:14 -0700 | [diff] [blame] | 480 | clearSelection(); |
Vikram Aggarwal | 4f4782b | 2012-05-30 08:39:09 -0700 | [diff] [blame] | 481 | mUpdater.refreshConversationList(); |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 482 | } |
| 483 | |
| 484 | /** |
| 485 | * Disable the selected conversations menu item associated with a command |
| 486 | * id. |
| 487 | */ |
| 488 | public void disableCommand(int id) { |
| 489 | enableMenuItem(id, false); |
| 490 | } |
| 491 | |
| 492 | /** |
| 493 | * Enable the selected conversations menu item associated with a command |
| 494 | * id. |
| 495 | */ |
| 496 | public void enableCommand(int id) { |
| 497 | enableMenuItem(id, true); |
| 498 | } |
| 499 | |
| 500 | private void enableMenuItem(int id, boolean enable) { |
| 501 | if (mActivated) { |
| 502 | MenuItem item = mMenu.findItem(id); |
| 503 | if (item != null) { |
| 504 | item.setEnabled(enable); |
| 505 | } |
| 506 | } |
| 507 | } |
Mindy Pereira | 11b0bbd | 2012-01-24 10:06:17 -0800 | [diff] [blame] | 508 | } |