Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2012 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.ui; |
| 19 | |
| 20 | import android.animation.Animator; |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 21 | import android.animation.AnimatorSet; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 22 | import android.animation.ObjectAnimator; |
| 23 | import android.content.Context; |
| 24 | import android.database.Cursor; |
Mindy Pereira | d3367499 | 2012-06-25 16:26:30 -0700 | [diff] [blame] | 25 | import android.os.Bundle; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 26 | import android.os.Handler; |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 27 | import android.view.LayoutInflater; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 28 | import android.view.View; |
| 29 | import android.view.ViewGroup; |
| 30 | import android.widget.SimpleCursorAdapter; |
| 31 | |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 32 | import com.android.mail.R; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 33 | import com.android.mail.browse.ConversationCursor; |
| 34 | import com.android.mail.browse.ConversationItemView; |
mindyp | b02ec69 | 2012-09-12 09:43:20 -0700 | [diff] [blame] | 35 | import com.android.mail.browse.ConversationItemViewCoordinates; |
Mindy Pereira | 1ef988f | 2012-07-24 12:06:03 -0700 | [diff] [blame] | 36 | import com.android.mail.browse.SwipeableConversationItemView; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 37 | import com.android.mail.providers.Account; |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 38 | import com.android.mail.providers.AccountObserver; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 39 | import com.android.mail.providers.Conversation; |
Mindy Pereira | 4584a0d | 2012-03-13 14:42:14 -0700 | [diff] [blame] | 40 | import com.android.mail.providers.Folder; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 41 | import com.android.mail.providers.UIProvider; |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 42 | import com.android.mail.ui.SwipeableListView.ListItemsRemovedListener; |
Paul Westbrook | b334c90 | 2012-06-25 11:42:46 -0700 | [diff] [blame] | 43 | import com.android.mail.utils.LogTag; |
Vikram Aggarwal | 2ae8126 | 2012-03-27 15:43:57 -0700 | [diff] [blame] | 44 | import com.android.mail.utils.LogUtils; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 45 | import com.google.common.collect.Maps; |
Marc Blank | 2596f00 | 2012-03-22 10:26:26 -0700 | [diff] [blame] | 46 | |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 47 | import java.util.ArrayList; |
Mindy Pereira | fac92d7 | 2012-02-01 10:58:33 -0800 | [diff] [blame] | 48 | import java.util.Collection; |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 49 | import java.util.HashMap; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 50 | import java.util.HashSet; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 51 | import java.util.Iterator; |
| 52 | import java.util.Map.Entry; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 53 | |
| 54 | public class AnimatedAdapter extends SimpleCursorAdapter implements |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 55 | android.animation.Animator.AnimatorListener { |
mindyp | a44babd | 2013-01-17 09:59:25 -0800 | [diff] [blame] | 56 | private static int sDismissAllShortDelay = -1; |
| 57 | private static int sDismissAllLongDelay = -1; |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 58 | private static final String LAST_DELETING_ITEMS = "last_deleting_items"; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 59 | private static final String LEAVE_BEHIND_ITEM_DATA = "leave_behind_item_data"; |
| 60 | private static final String LEAVE_BEHIND_ITEM_ID = "leave_behind_item_id"; |
Vikram Aggarwal | b54a467 | 2012-04-11 11:32:44 -0700 | [diff] [blame] | 61 | private final static int TYPE_VIEW_CONVERSATION = 0; |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 62 | private final static int TYPE_VIEW_FOOTER = 1; |
mindyp | d0f9578 | 2012-10-09 13:51:40 -0700 | [diff] [blame] | 63 | private final static int TYPE_VIEW_DONT_RECYCLE = -1; |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 64 | private final HashSet<Long> mDeletingItems = new HashSet<Long>(); |
| 65 | private final ArrayList<Long> mLastDeletingItems = new ArrayList<Long>(); |
| 66 | private final HashSet<Long> mUndoingItems = new HashSet<Long>(); |
| 67 | private final HashSet<Long> mSwipeDeletingItems = new HashSet<Long>(); |
| 68 | private final HashSet<Long> mSwipeUndoingItems = new HashSet<Long>(); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 69 | private final HashMap<Long, SwipeableConversationItemView> mAnimatingViews = |
| 70 | new HashMap<Long, SwipeableConversationItemView>(); |
Mindy Pereira | 1e946db | 2012-08-12 12:05:06 -0700 | [diff] [blame] | 71 | private final HashMap<Long, LeaveBehindItem> mFadeLeaveBehindItems = |
Mindy Pereira | 8f39702 | 2012-08-12 11:19:15 -0700 | [diff] [blame] | 72 | new HashMap<Long, LeaveBehindItem>(); |
Vikram Aggarwal | 33519c0 | 2012-07-31 16:45:31 -0700 | [diff] [blame] | 73 | /** The current account */ |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 74 | private Account mAccount; |
Vikram Aggarwal | 33519c0 | 2012-07-31 16:45:31 -0700 | [diff] [blame] | 75 | private final Context mContext; |
| 76 | private final ConversationSelectionSet mBatchConversations; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 77 | private Runnable mCountDown; |
| 78 | private Handler mHandler; |
| 79 | protected long mLastLeaveBehind = -1; |
| 80 | |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 81 | /** |
| 82 | * The next action to perform. Do not read or write this. All accesses should |
| 83 | * be in {@link #performAndSetNextAction(DestructiveAction)} which commits the |
| 84 | * previous action, if any. |
| 85 | */ |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 86 | private ListItemsRemovedListener mPendingDestruction; |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 87 | /** |
| 88 | * A destructive action that refreshes the list and performs no other action. |
| 89 | */ |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 90 | private final ListItemsRemovedListener mRefreshAction = new ListItemsRemovedListener() { |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 91 | @Override |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 92 | public void onListItemsRemoved() { |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 93 | notifyDataSetChanged(); |
| 94 | } |
| 95 | }; |
| 96 | |
Paul Westbrook | cff1aea | 2012-08-10 11:51:00 -0700 | [diff] [blame] | 97 | public interface Listener { |
| 98 | void onAnimationEnd(AnimatedAdapter adapter); |
| 99 | } |
| 100 | |
Mindy Pereira | 6681f6b | 2012-03-09 13:55:54 -0800 | [diff] [blame] | 101 | private View mFooter; |
| 102 | private boolean mShowFooter; |
Mindy Pereira | 4584a0d | 2012-03-13 14:42:14 -0700 | [diff] [blame] | 103 | private Folder mFolder; |
Mindy Pereira | 07118a0 | 2012-04-02 16:35:01 -0700 | [diff] [blame] | 104 | private final SwipeableListView mListView; |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 105 | private boolean mSwipeEnabled; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 106 | private final HashMap<Long, LeaveBehindItem> mLeaveBehindItems = Maps.newHashMap(); |
Vikram Aggarwal | 33519c0 | 2012-07-31 16:45:31 -0700 | [diff] [blame] | 107 | /** True if priority inbox markers are enabled, false otherwise. */ |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 108 | private boolean mPriorityMarkersEnabled; |
Mindy Pereira | e7d783d | 2012-08-07 14:06:34 -0700 | [diff] [blame] | 109 | private ControllableActivity mActivity; |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 110 | private final AccountObserver mAccountListener = new AccountObserver() { |
| 111 | @Override |
| 112 | public void onChanged(Account newAccount) { |
| 113 | setAccount(newAccount); |
| 114 | notifyDataSetChanged(); |
| 115 | } |
| 116 | }; |
| 117 | |
| 118 | private final void setAccount(Account newAccount) { |
| 119 | mAccount = newAccount; |
| 120 | mPriorityMarkersEnabled = mAccount.settings.priorityArrowsEnabled; |
| 121 | mSwipeEnabled = mAccount.supportsCapability(UIProvider.AccountCapabilities.UNDO); |
| 122 | } |
| 123 | |
Vikram Aggarwal | 2ae8126 | 2012-03-27 15:43:57 -0700 | [diff] [blame] | 124 | /** |
| 125 | * Used only for debugging. |
| 126 | */ |
Paul Westbrook | b334c90 | 2012-06-25 11:42:46 -0700 | [diff] [blame] | 127 | private static final String LOG_TAG = LogTag.getLogTag(); |
mindyp | a44babd | 2013-01-17 09:59:25 -0800 | [diff] [blame] | 128 | private static final int INCREASE_WAIT_COUNT = 2; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 129 | |
| 130 | public AnimatedAdapter(Context context, int textViewResourceId, ConversationCursor cursor, |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 131 | ConversationSelectionSet batch, |
Mindy Pereira | e7d783d | 2012-08-07 14:06:34 -0700 | [diff] [blame] | 132 | ControllableActivity activity, SwipeableListView listView) { |
Andy Huang | 2c4e6dc | 2012-07-25 18:02:59 -0700 | [diff] [blame] | 133 | super(context, textViewResourceId, cursor, UIProvider.CONVERSATION_PROJECTION, null, 0); |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 134 | mContext = context; |
| 135 | mBatchConversations = batch; |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 136 | setAccount(mAccountListener.initialize(activity.getAccountController())); |
Mindy Pereira | e7d783d | 2012-08-07 14:06:34 -0700 | [diff] [blame] | 137 | mActivity = activity; |
Mindy Pereira | 6681f6b | 2012-03-09 13:55:54 -0800 | [diff] [blame] | 138 | mShowFooter = false; |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 139 | mListView = listView; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 140 | mHandler = new Handler(); |
mindyp | a44babd | 2013-01-17 09:59:25 -0800 | [diff] [blame] | 141 | if (sDismissAllShortDelay == -1) { |
| 142 | sDismissAllShortDelay = |
| 143 | context.getResources() |
| 144 | .getInteger(R.integer.dismiss_all_leavebehinds_short_delay); |
| 145 | sDismissAllLongDelay = |
| 146 | context.getResources() |
| 147 | .getInteger(R.integer.dismiss_all_leavebehinds_long_delay); |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 148 | } |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 149 | } |
| 150 | |
mindyp | 3b5e82b | 2012-12-18 14:23:20 -0800 | [diff] [blame] | 151 | public void cancelDismissCounter() { |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 152 | cancelLeaveBehindFadeInAnimation(); |
mindyp | 3b5e82b | 2012-12-18 14:23:20 -0800 | [diff] [blame] | 153 | mHandler.removeCallbacks(mCountDown); |
| 154 | } |
| 155 | |
| 156 | public void startDismissCounter() { |
mindyp | a44babd | 2013-01-17 09:59:25 -0800 | [diff] [blame] | 157 | if (mLeaveBehindItems.size() > INCREASE_WAIT_COUNT) { |
| 158 | mHandler.postDelayed(mCountDown, sDismissAllLongDelay); |
| 159 | } else { |
| 160 | mHandler.postDelayed(mCountDown, sDismissAllShortDelay); |
| 161 | } |
mindyp | 3b5e82b | 2012-12-18 14:23:20 -0800 | [diff] [blame] | 162 | } |
| 163 | |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 164 | public final void destroy() { |
| 165 | // Set a null cursor in the adapter |
| 166 | swapCursor(null); |
| 167 | mAccountListener.unregisterAndDestroy(); |
Mindy Pereira | 6681f6b | 2012-03-09 13:55:54 -0800 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | @Override |
| 171 | public int getCount() { |
Vikram Aggarwal | b54a467 | 2012-04-11 11:32:44 -0700 | [diff] [blame] | 172 | final int count = super.getCount(); |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 173 | return mShowFooter ? count + 1 : count; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 174 | } |
| 175 | |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 176 | public void setUndo(boolean undo) { |
mindyp | 3b5e82b | 2012-12-18 14:23:20 -0800 | [diff] [blame] | 177 | if (undo) { |
| 178 | if (!mLastDeletingItems.isEmpty()) { |
| 179 | mUndoingItems.addAll(mLastDeletingItems); |
| 180 | mLastDeletingItems.clear(); |
| 181 | } |
| 182 | if (mLastLeaveBehind != -1) { |
| 183 | mUndoingItems.add(mLastLeaveBehind); |
| 184 | mLastLeaveBehind = -1; |
| 185 | } |
Marc Blank | 2596f00 | 2012-03-22 10:26:26 -0700 | [diff] [blame] | 186 | // Start animation |
| 187 | notifyDataSetChanged(); |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 188 | performAndSetNextAction(mRefreshAction); |
Marc Blank | 3b2d913 | 2012-02-05 12:21:10 -0800 | [diff] [blame] | 189 | } |
| 190 | } |
| 191 | |
Mindy Pereira | bdb43d5 | 2012-07-25 17:46:00 -0700 | [diff] [blame] | 192 | public void setSwipeUndo(boolean undo) { |
mindyp | 3b5e82b | 2012-12-18 14:23:20 -0800 | [diff] [blame] | 193 | if (undo) { |
| 194 | if (!mLastDeletingItems.isEmpty()) { |
| 195 | mSwipeUndoingItems.addAll(mLastDeletingItems); |
| 196 | mLastDeletingItems.clear(); |
| 197 | } |
| 198 | if (mLastLeaveBehind != -1) { |
| 199 | mSwipeUndoingItems.add(mLastLeaveBehind); |
| 200 | mLastLeaveBehind = -1; |
| 201 | } |
Mindy Pereira | bdb43d5 | 2012-07-25 17:46:00 -0700 | [diff] [blame] | 202 | // Start animation |
| 203 | notifyDataSetChanged(); |
| 204 | performAndSetNextAction(mRefreshAction); |
| 205 | } |
| 206 | } |
| 207 | |
mindyp | 0abee55 | 2012-08-30 12:34:22 -0700 | [diff] [blame] | 208 | public View createConversationItemView(SwipeableConversationItemView view, Context context, |
| 209 | Conversation conv) { |
| 210 | if (view == null) { |
| 211 | view = new SwipeableConversationItemView(context, mAccount.name); |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 212 | } |
Marc Blank | 87557c1 | 2012-09-23 12:44:59 -0700 | [diff] [blame] | 213 | view.bind(conv, mActivity, mBatchConversations, mFolder, |
Paul Westbrook | 7ed5377 | 2013-01-23 10:19:55 -0800 | [diff] [blame^] | 214 | mAccount != null ? mAccount.settings.hideCheckboxes : false, mSwipeEnabled, |
mindyp | 0abee55 | 2012-08-30 12:34:22 -0700 | [diff] [blame] | 215 | mPriorityMarkersEnabled, this); |
| 216 | return view; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 217 | } |
| 218 | |
Marc Blank | dd10bc8 | 2012-02-01 19:10:46 -0800 | [diff] [blame] | 219 | @Override |
| 220 | public boolean hasStableIds() { |
| 221 | return true; |
| 222 | } |
| 223 | |
| 224 | @Override |
| 225 | public int getViewTypeCount() { |
Mindy Pereira | 4649394 | 2012-04-13 09:09:52 -0700 | [diff] [blame] | 226 | // TYPE_VIEW_CONVERSATION, TYPE_VIEW_DELETING, TYPE_VIEW_UNDOING, and |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 227 | // TYPE_VIEW_FOOTER, TYPE_VIEW_LEAVEBEHIND. |
| 228 | return 5; |
Marc Blank | dd10bc8 | 2012-02-01 19:10:46 -0800 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | @Override |
| 232 | public int getItemViewType(int position) { |
Vikram Aggarwal | b54a467 | 2012-04-11 11:32:44 -0700 | [diff] [blame] | 233 | // Try to recycle views. |
Vikram Aggarwal | b54a467 | 2012-04-11 11:32:44 -0700 | [diff] [blame] | 234 | if (mShowFooter && position == super.getCount()) { |
| 235 | return TYPE_VIEW_FOOTER; |
mindyp | d0f9578 | 2012-10-09 13:51:40 -0700 | [diff] [blame] | 236 | } else if (hasLeaveBehinds() || isAnimating()) { |
| 237 | // Setting as type -1 means the recycler won't take this view and |
| 238 | // return it in get view. This is a bit of a "hammer" in that it |
| 239 | // won't let even safe views be recycled here, |
| 240 | // but its safer and cheaper than trying to determine individual |
| 241 | // types. In a future release, use position/id map to try to make |
| 242 | // this cleaner / faster to determine if the view is animating. |
| 243 | return TYPE_VIEW_DONT_RECYCLE; |
Vikram Aggarwal | b54a467 | 2012-04-11 11:32:44 -0700 | [diff] [blame] | 244 | } |
| 245 | return TYPE_VIEW_CONVERSATION; |
Marc Blank | dd10bc8 | 2012-02-01 19:10:46 -0800 | [diff] [blame] | 246 | } |
| 247 | |
Vikram Aggarwal | 54452ae | 2012-03-13 15:29:00 -0700 | [diff] [blame] | 248 | /** |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 249 | * Deletes the selected conversations from the conversation list view with a |
| 250 | * translation and then a shrink. These conversations <b>must</b> have their |
| 251 | * {@link Conversation#position} set to the position of these conversations |
| 252 | * among the list. This will only remove the element from the list. The job |
| 253 | * of deleting the actual element is left to the the listener. This listener |
| 254 | * will be called when the animations are complete and is required to delete |
| 255 | * the conversation. |
| 256 | * @param conversations |
| 257 | * @param listener |
| 258 | */ |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 259 | public void swipeDelete(Collection<Conversation> conversations, |
| 260 | ListItemsRemovedListener listener) { |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 261 | delete(conversations, listener, mSwipeDeletingItems); |
| 262 | } |
| 263 | |
| 264 | |
| 265 | /** |
| 266 | * Deletes the selected conversations from the conversation list view by |
| 267 | * shrinking them away. These conversations <b>must</b> have their |
| 268 | * {@link Conversation#position} set to the position of these conversations |
| 269 | * among the list. This will only remove the element from the list. The job |
| 270 | * of deleting the actual element is left to the the listener. This listener |
| 271 | * will be called when the animations are complete and is required to delete |
| 272 | * the conversation. |
Vikram Aggarwal | 54452ae | 2012-03-13 15:29:00 -0700 | [diff] [blame] | 273 | * @param conversations |
| 274 | * @param listener |
| 275 | */ |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 276 | public void delete(Collection<Conversation> conversations, ListItemsRemovedListener listener) { |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 277 | delete(conversations, listener, mDeletingItems); |
Mindy Pereira | fac92d7 | 2012-02-01 10:58:33 -0800 | [diff] [blame] | 278 | } |
| 279 | |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 280 | private void delete(Collection<Conversation> conversations, ListItemsRemovedListener listener, |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 281 | HashSet<Long> list) { |
Marc Blank | 3b2d913 | 2012-02-05 12:21:10 -0800 | [diff] [blame] | 282 | // Clear out any remaining items and add the new ones |
| 283 | mLastDeletingItems.clear(); |
Paul Westbrook | e4abda7 | 2012-10-15 18:23:46 -0700 | [diff] [blame] | 284 | // Since we are deleting new items, clear any remaining undo items |
| 285 | mUndoingItems.clear(); |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 286 | |
Vikram Aggarwal | b54a467 | 2012-04-11 11:32:44 -0700 | [diff] [blame] | 287 | final int startPosition = mListView.getFirstVisiblePosition(); |
| 288 | final int endPosition = mListView.getLastVisiblePosition(); |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 289 | |
| 290 | // Only animate visible items |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 291 | for (Conversation c: conversations) { |
| 292 | if (c.position >= startPosition && c.position <= endPosition) { |
| 293 | mLastDeletingItems.add(c.id); |
| 294 | list.add(c.id); |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 295 | } |
| 296 | } |
| 297 | |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 298 | if (list.isEmpty()) { |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 299 | // If we have no deleted items on screen, skip the animation |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 300 | listener.onListItemsRemoved(); |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 301 | } else { |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 302 | performAndSetNextAction(listener); |
Marc Blank | 66bee6e | 2012-04-01 15:02:24 -0700 | [diff] [blame] | 303 | } |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 304 | notifyDataSetChanged(); |
| 305 | } |
| 306 | |
| 307 | @Override |
| 308 | public View getView(int position, View convertView, ViewGroup parent) { |
Mindy Pereira | 6681f6b | 2012-03-09 13:55:54 -0800 | [diff] [blame] | 309 | if (mShowFooter && position == super.getCount()) { |
| 310 | return mFooter; |
| 311 | } |
mindyp | 0abee55 | 2012-08-30 12:34:22 -0700 | [diff] [blame] | 312 | ConversationCursor cursor = (ConversationCursor) getItem(position); |
| 313 | Conversation conv = new Conversation(cursor); |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 314 | if (isPositionUndoing(conv.id)) { |
| 315 | return getUndoingView(position, conv, parent, false /* don't show swipe background */); |
| 316 | } if (isPositionUndoingSwipe(conv.id)) { |
| 317 | return getUndoingView(position, conv, parent, true /* show swipe background */); |
| 318 | } else if (isPositionDeleting(conv.id)) { |
| 319 | return getDeletingView(position, conv, parent, false); |
| 320 | } else if (isPositionSwipeDeleting(conv.id)) { |
| 321 | return getDeletingView(position, conv, parent, true); |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 322 | } |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 323 | if (hasFadeLeaveBehinds()) { |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 324 | if(isPositionFadeLeaveBehind(conv)) { |
| 325 | LeaveBehindItem fade = getFadeLeaveBehindItem(position, conv); |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 326 | fade.startShrinkAnimation(mActivity.getViewMode(), this); |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 327 | return fade; |
| 328 | } |
| 329 | } |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 330 | if (hasLeaveBehinds()) { |
mindyp | e358ed5 | 2012-11-26 09:25:25 -0800 | [diff] [blame] | 331 | if (isPositionLeaveBehind(conv)) { |
Mindy Pereira | 84f9b5c | 2012-08-16 10:40:47 -0700 | [diff] [blame] | 332 | LeaveBehindItem fadeIn = getLeaveBehindItem(conv); |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 333 | if (conv.id == mLastLeaveBehind) { |
mindyp | a44babd | 2013-01-17 09:59:25 -0800 | [diff] [blame] | 334 | // If it looks like the person is doing a lot of rapid |
| 335 | // swipes, wait patiently before animating |
| 336 | if (mLeaveBehindItems.size() > INCREASE_WAIT_COUNT) { |
| 337 | if (fadeIn.isAnimating()) { |
| 338 | fadeIn.increaseFadeInDelay(sDismissAllLongDelay); |
| 339 | } else { |
| 340 | fadeIn.startFadeInTextAnimation(sDismissAllLongDelay); |
| 341 | } |
| 342 | } else { |
| 343 | // Otherwise, assume they are just doing 1 and wait less time |
| 344 | fadeIn.startFadeInTextAnimation(sDismissAllShortDelay /* delay start */); |
| 345 | } |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 346 | } |
Mindy Pereira | 84f9b5c | 2012-08-16 10:40:47 -0700 | [diff] [blame] | 347 | return fadeIn; |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 348 | } |
| 349 | } |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 350 | if (convertView != null && !(convertView instanceof SwipeableConversationItemView)) { |
| 351 | LogUtils.w(LOG_TAG, "Incorrect convert view received; nulling it out"); |
mindyp | eb88ece | 2012-10-09 11:54:09 -0700 | [diff] [blame] | 352 | convertView = newView(mContext, cursor, parent); |
Mindy Pereira | c79aec7 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 353 | } else if (convertView != null) { |
| 354 | ((SwipeableConversationItemView) convertView).reset(); |
Mindy Pereira | afd6f92 | 2012-03-16 15:21:33 -0700 | [diff] [blame] | 355 | } |
mindyp | 0abee55 | 2012-08-30 12:34:22 -0700 | [diff] [blame] | 356 | return createConversationItemView((SwipeableConversationItemView) convertView, mContext, |
| 357 | conv); |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 358 | } |
| 359 | |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 360 | private boolean hasLeaveBehinds() { |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 361 | return !mLeaveBehindItems.isEmpty(); |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 362 | } |
| 363 | |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 364 | private boolean hasFadeLeaveBehinds() { |
Mindy Pereira | 8f39702 | 2012-08-12 11:19:15 -0700 | [diff] [blame] | 365 | return !mFadeLeaveBehindItems.isEmpty(); |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 366 | } |
| 367 | |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 368 | public LeaveBehindItem setupLeaveBehind(Conversation target, ToastBarOperation undoOp, |
| 369 | int deletedRow) { |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 370 | cancelLeaveBehindFadeInAnimation(); |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 371 | mLastLeaveBehind = target.id; |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 372 | fadeOutLeaveBehindItems(); |
mindyp | b02ec69 | 2012-09-12 09:43:20 -0700 | [diff] [blame] | 373 | boolean isWide = ConversationItemViewCoordinates.isWideMode(ConversationItemViewCoordinates |
| 374 | .getMode(mContext, mActivity.getViewMode())); |
Mindy Pereira | dea5d77 | 2012-04-17 08:13:39 -0700 | [diff] [blame] | 375 | LeaveBehindItem leaveBehind = (LeaveBehindItem) LayoutInflater.from(mContext).inflate( |
mindyp | b02ec69 | 2012-09-12 09:43:20 -0700 | [diff] [blame] | 376 | isWide? R.layout.swipe_leavebehind_wide : R.layout.swipe_leavebehind, null); |
Mindy Pereira | bcd784c | 2012-08-10 09:53:24 -0700 | [diff] [blame] | 377 | leaveBehind.bindOperations(deletedRow, mAccount, this, undoOp, target, mFolder); |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 378 | mLeaveBehindItems.put(target.id, leaveBehind); |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 379 | mLastDeletingItems.add(target.id); |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 380 | return leaveBehind; |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 381 | } |
| 382 | |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 383 | public void fadeOutSpecificLeaveBehindItem(long id) { |
| 384 | if (mLastLeaveBehind == id) { |
| 385 | mLastLeaveBehind = -1; |
| 386 | } |
| 387 | startFadeOutLeaveBehindItemsAnimations(); |
| 388 | } |
| 389 | |
| 390 | // This should kick off a timer such that there is a minimum time each item |
| 391 | // shows up before being dismissed. That way if the user is swiping away |
| 392 | // items in rapid succession, their finger position is maintained. |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 393 | public void fadeOutLeaveBehindItems() { |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 394 | if (mCountDown == null) { |
| 395 | mCountDown = new Runnable() { |
| 396 | @Override |
| 397 | public void run() { |
| 398 | startFadeOutLeaveBehindItemsAnimations(); |
| 399 | } |
| 400 | }; |
| 401 | } else { |
| 402 | mHandler.removeCallbacks(mCountDown); |
| 403 | } |
| 404 | // Clear all the text since these are no longer clickable |
| 405 | Iterator<Entry<Long, LeaveBehindItem>> i = mLeaveBehindItems.entrySet().iterator(); |
| 406 | LeaveBehindItem item; |
| 407 | while (i.hasNext()) { |
| 408 | item = i.next().getValue(); |
| 409 | Conversation conv = item.getData(); |
| 410 | if (mLastLeaveBehind == -1 || conv.id != mLastLeaveBehind) { |
mindyp | a44babd | 2013-01-17 09:59:25 -0800 | [diff] [blame] | 411 | item.cancelFadeInTextAnimation(); |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 412 | item.makeInert(); |
| 413 | } |
| 414 | } |
mindyp | 3b5e82b | 2012-12-18 14:23:20 -0800 | [diff] [blame] | 415 | startDismissCounter(); |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 416 | } |
| 417 | |
| 418 | protected void startFadeOutLeaveBehindItemsAnimations() { |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 419 | final int startPosition = mListView.getFirstVisiblePosition(); |
| 420 | final int endPosition = mListView.getLastVisiblePosition(); |
| 421 | |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 422 | if (hasLeaveBehinds()) { |
| 423 | // If the item is visible, fade it out. Otherwise, just remove |
| 424 | // it. |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 425 | Iterator<Entry<Long, LeaveBehindItem>> i = mLeaveBehindItems.entrySet().iterator(); |
| 426 | LeaveBehindItem item; |
| 427 | while (i.hasNext()) { |
| 428 | item = i.next().getValue(); |
| 429 | Conversation conv = item.getData(); |
| 430 | if (mLastLeaveBehind == -1 || conv.id != mLastLeaveBehind) { |
| 431 | if (conv.position >= startPosition && conv.position <= endPosition) { |
| 432 | mFadeLeaveBehindItems.put(conv.id, item); |
| 433 | } else { |
| 434 | item.commit(); |
| 435 | } |
| 436 | i.remove(); |
| 437 | } |
Mindy Pereira | dea5d77 | 2012-04-17 08:13:39 -0700 | [diff] [blame] | 438 | } |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 439 | cancelLeaveBehindFadeInAnimation(); |
Mindy Pereira | dea5d77 | 2012-04-17 08:13:39 -0700 | [diff] [blame] | 440 | } |
Mindy Pereira | 06b6238 | 2012-07-24 14:31:20 -0700 | [diff] [blame] | 441 | if (!mLastDeletingItems.isEmpty()) { |
| 442 | mLastDeletingItems.clear(); |
| 443 | } |
Mindy Pereira | 1e2573b | 2012-04-17 14:34:36 -0700 | [diff] [blame] | 444 | notifyDataSetChanged(); |
Mindy Pereira | dea5d77 | 2012-04-17 08:13:39 -0700 | [diff] [blame] | 445 | } |
| 446 | |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 447 | private void cancelLeaveBehindFadeInAnimation() { |
| 448 | LeaveBehindItem leaveBehind = getLastLeaveBehindItem(); |
| 449 | if (leaveBehind != null) { |
| 450 | leaveBehind.cancelFadeInTextAnimation(); |
| 451 | } |
| 452 | } |
| 453 | |
mindyp | 5254486 | 2012-08-20 12:05:36 -0700 | [diff] [blame] | 454 | public SwipeableListView getListView() { |
| 455 | return mListView; |
| 456 | } |
| 457 | |
mindyp | c6adce3 | 2012-08-22 18:46:42 -0700 | [diff] [blame] | 458 | public void commitLeaveBehindItems(boolean animate) { |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 459 | // Remove any previously existing leave behinds. |
mindyp | 5254486 | 2012-08-20 12:05:36 -0700 | [diff] [blame] | 460 | boolean changed = false; |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 461 | if (hasLeaveBehinds()) { |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 462 | for (LeaveBehindItem item : mLeaveBehindItems.values()) { |
| 463 | if (animate) { |
mindyp | dcb26ae | 2012-11-26 13:25:05 -0800 | [diff] [blame] | 464 | mFadeLeaveBehindItems.put(item.getConversationId(), item); |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 465 | } else { |
| 466 | item.commit(); |
| 467 | } |
mindyp | c6adce3 | 2012-08-22 18:46:42 -0700 | [diff] [blame] | 468 | } |
mindyp | 5254486 | 2012-08-20 12:05:36 -0700 | [diff] [blame] | 469 | changed = true; |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 470 | mLastLeaveBehind = -1; |
| 471 | mLeaveBehindItems.clear(); |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 472 | } |
mindyp | aa55bc9 | 2012-08-24 09:49:56 -0700 | [diff] [blame] | 473 | if (hasFadeLeaveBehinds() && !animate) { |
| 474 | // Find any fading leave behind items and commit them all, too. |
| 475 | for (LeaveBehindItem item : mFadeLeaveBehindItems.values()) { |
| 476 | item.commit(); |
| 477 | } |
| 478 | mFadeLeaveBehindItems.clear(); |
| 479 | changed = true; |
| 480 | } |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 481 | if (!mLastDeletingItems.isEmpty()) { |
| 482 | mLastDeletingItems.clear(); |
mindyp | 5254486 | 2012-08-20 12:05:36 -0700 | [diff] [blame] | 483 | changed = true; |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 484 | } |
mindyp | 5254486 | 2012-08-20 12:05:36 -0700 | [diff] [blame] | 485 | if (changed) { |
| 486 | notifyDataSetChanged(); |
| 487 | } |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 488 | } |
| 489 | |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 490 | private LeaveBehindItem getLeaveBehindItem(Conversation target) { |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 491 | return mLeaveBehindItems.get(target.id); |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 492 | } |
| 493 | |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 494 | private LeaveBehindItem getFadeLeaveBehindItem(int position, Conversation target) { |
Mindy Pereira | 8f39702 | 2012-08-12 11:19:15 -0700 | [diff] [blame] | 495 | return mFadeLeaveBehindItems.get(target.id); |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 496 | } |
| 497 | |
Mindy Pereira | d3651a6 | 2012-04-04 12:23:40 -0700 | [diff] [blame] | 498 | @Override |
| 499 | public long getItemId(int position) { |
| 500 | if (mShowFooter && position == super.getCount()) { |
| 501 | return -1; |
| 502 | } |
| 503 | return super.getItemId(position); |
| 504 | } |
| 505 | |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 506 | private View getDeletingView(int position, Conversation conversation, ViewGroup parent, |
| 507 | boolean swipe) { |
Vikram Aggarwal | c7d201d | 2012-02-15 13:21:10 -0800 | [diff] [blame] | 508 | conversation.position = position; |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 509 | SwipeableConversationItemView deletingView = mAnimatingViews.get(conversation.id); |
| 510 | if (deletingView == null) { |
| 511 | // The undo animation consists of fading in the conversation that |
| 512 | // had been destroyed. |
| 513 | deletingView = newConversationItemView(position, parent, conversation); |
| 514 | deletingView.startDeleteAnimation(this, swipe); |
| 515 | } |
| 516 | return deletingView; |
Mindy Pereira | a41baad | 2012-04-13 08:30:54 -0700 | [diff] [blame] | 517 | } |
| 518 | |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 519 | private View getUndoingView(int position, Conversation conv, ViewGroup parent, boolean swipe) { |
| 520 | conv.position = position; |
| 521 | SwipeableConversationItemView undoView = mAnimatingViews.get(conv.id); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 522 | if (undoView == null) { |
| 523 | // The undo animation consists of fading in the conversation that |
| 524 | // had been destroyed. |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 525 | undoView = newConversationItemView(position, parent, conv); |
Mindy Pereira | 84f9b5c | 2012-08-16 10:40:47 -0700 | [diff] [blame] | 526 | undoView.startUndoAnimation(mActivity.getViewMode(), this, swipe); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 527 | } |
| 528 | return undoView; |
| 529 | } |
| 530 | |
mindyp | 5ceab3b | 2012-08-30 16:41:59 -0700 | [diff] [blame] | 531 | @Override |
| 532 | public View newView(Context context, Cursor cursor, ViewGroup parent) { |
| 533 | SwipeableConversationItemView view = new SwipeableConversationItemView(context, |
| 534 | mAccount.name); |
| 535 | return view; |
| 536 | } |
| 537 | |
| 538 | @Override |
| 539 | public void bindView(View view, Context context, Cursor cursor) { |
| 540 | if (! (view instanceof SwipeableConversationItemView)) { |
| 541 | return; |
| 542 | } |
| 543 | ((SwipeableConversationItemView) view).bind(cursor, mActivity, mBatchConversations, mFolder, |
Paul Westbrook | 7ed5377 | 2013-01-23 10:19:55 -0800 | [diff] [blame^] | 544 | mAccount != null ? mAccount.settings.hideCheckboxes : false, |
mindyp | 5ceab3b | 2012-08-30 16:41:59 -0700 | [diff] [blame] | 545 | mSwipeEnabled, mPriorityMarkersEnabled, this); |
| 546 | } |
| 547 | |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 548 | private SwipeableConversationItemView newConversationItemView(int position, ViewGroup parent, |
| 549 | Conversation conversation) { |
| 550 | SwipeableConversationItemView view = (SwipeableConversationItemView) super.getView( |
| 551 | position, null, parent); |
mindyp | dc0617f | 2012-08-31 14:30:18 -0700 | [diff] [blame] | 552 | view.reset(); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 553 | view.bind(conversation, mActivity, mBatchConversations, mFolder, |
Paul Westbrook | 7ed5377 | 2013-01-23 10:19:55 -0800 | [diff] [blame^] | 554 | mAccount != null ? mAccount.settings.hideCheckboxes : false, mSwipeEnabled, |
Vikram Aggarwal | 33519c0 | 2012-07-31 16:45:31 -0700 | [diff] [blame] | 555 | mPriorityMarkersEnabled, this); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 556 | mAnimatingViews.put(conversation.id, view); |
| 557 | return view; |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 558 | } |
| 559 | |
Mindy Pereira | d3651a6 | 2012-04-04 12:23:40 -0700 | [diff] [blame] | 560 | @Override |
| 561 | public Object getItem(int position) { |
| 562 | if (mShowFooter && position == super.getCount()) { |
| 563 | return mFooter; |
| 564 | } |
| 565 | return super.getItem(position); |
| 566 | } |
| 567 | |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 568 | private boolean isPositionDeleting(long id) { |
| 569 | return mDeletingItems.contains(id); |
Mindy Pereira | a41baad | 2012-04-13 08:30:54 -0700 | [diff] [blame] | 570 | } |
| 571 | |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 572 | private boolean isPositionSwipeDeleting(long id) { |
| 573 | return mSwipeDeletingItems.contains(id); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 574 | } |
| 575 | |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 576 | private boolean isPositionUndoing(long id) { |
| 577 | return mUndoingItems.contains(id); |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 578 | } |
| 579 | |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 580 | private boolean isPositionUndoingSwipe(long id) { |
| 581 | return mSwipeUndoingItems.contains(id); |
Mindy Pereira | bdb43d5 | 2012-07-25 17:46:00 -0700 | [diff] [blame] | 582 | } |
| 583 | |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 584 | private boolean isPositionLeaveBehind(Conversation conv) { |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 585 | return hasLeaveBehinds() |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 586 | && mLeaveBehindItems.containsKey(conv.id) |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 587 | && conv.isMostlyDead(); |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 588 | } |
| 589 | |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 590 | private boolean isPositionFadeLeaveBehind(Conversation conv) { |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 591 | return hasFadeLeaveBehinds() |
Mindy Pereira | 8f39702 | 2012-08-12 11:19:15 -0700 | [diff] [blame] | 592 | && mFadeLeaveBehindItems.containsKey(conv.id) |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 593 | && conv.isMostlyDead(); |
Andy Huang | 62d7962 | 2012-07-24 18:50:34 -0700 | [diff] [blame] | 594 | } |
| 595 | |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 596 | @Override |
| 597 | public void onAnimationStart(Animator animation) { |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 598 | if (!mUndoingItems.isEmpty()) { |
Mindy Pereira | 866d319 | 2012-03-26 09:50:00 -0700 | [diff] [blame] | 599 | mDeletingItems.clear(); |
| 600 | mLastDeletingItems.clear(); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 601 | mSwipeDeletingItems.clear(); |
Mindy Pereira | a41baad | 2012-04-13 08:30:54 -0700 | [diff] [blame] | 602 | } else { |
| 603 | mUndoingItems.clear(); |
Mindy Pereira | 866d319 | 2012-03-26 09:50:00 -0700 | [diff] [blame] | 604 | } |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 605 | } |
| 606 | |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 607 | /** |
| 608 | * Performs the pending destruction, if any and assigns the next pending action. |
| 609 | * @param next The next action that is to be performed, possibly null (if no next action is |
| 610 | * needed). |
| 611 | */ |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 612 | private final void performAndSetNextAction(ListItemsRemovedListener next) { |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 613 | if (mPendingDestruction != null) { |
mindyp | 9365a82 | 2012-09-12 09:09:09 -0700 | [diff] [blame] | 614 | mPendingDestruction.onListItemsRemoved(); |
Vikram Aggarwal | c30fe41 | 2012-05-18 11:58:58 -0700 | [diff] [blame] | 615 | } |
| 616 | mPendingDestruction = next; |
| 617 | } |
| 618 | |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 619 | @Override |
| 620 | public void onAnimationEnd(Animator animation) { |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 621 | Object obj; |
| 622 | if (animation instanceof AnimatorSet) { |
| 623 | AnimatorSet set = (AnimatorSet) animation; |
| 624 | obj = ((ObjectAnimator) set.getChildAnimations().get(0)).getTarget(); |
| 625 | } else { |
| 626 | obj = ((ObjectAnimator) animation).getTarget(); |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 627 | } |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 628 | updateAnimatingConversationItems(obj, mSwipeDeletingItems); |
| 629 | updateAnimatingConversationItems(obj, mDeletingItems); |
| 630 | updateAnimatingConversationItems(obj, mSwipeUndoingItems); |
| 631 | updateAnimatingConversationItems(obj, mUndoingItems); |
| 632 | if (hasFadeLeaveBehinds() && obj instanceof LeaveBehindItem) { |
| 633 | LeaveBehindItem objItem = (LeaveBehindItem) obj; |
| 634 | clearLeaveBehind(objItem.getConversationId()); |
| 635 | objItem.commit(); |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 636 | if (!hasFadeLeaveBehinds()) { |
mindyp | ac2ef81 | 2013-01-17 09:10:04 -0800 | [diff] [blame] | 637 | // Cancel any existing animations on the remaining leave behind |
| 638 | // item and start fading in text immediately. |
mindyp | ac2ef81 | 2013-01-17 09:10:04 -0800 | [diff] [blame] | 639 | LeaveBehindItem item = getLastLeaveBehindItem(); |
| 640 | if (item != null) { |
mindyp | a44babd | 2013-01-17 09:59:25 -0800 | [diff] [blame] | 641 | boolean cancelled = item.cancelFadeInTextAnimationIfNotStarted(); |
| 642 | if (cancelled) { |
| 643 | item.startFadeInTextAnimation(0 /* delay start */); |
| 644 | } |
mindyp | ac2ef81 | 2013-01-17 09:10:04 -0800 | [diff] [blame] | 645 | } |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 646 | } |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 647 | // The view types have changed, since the animating views are gone. |
| 648 | notifyDataSetChanged(); |
| 649 | } |
Paul Westbrook | cff1aea | 2012-08-10 11:51:00 -0700 | [diff] [blame] | 650 | |
| 651 | if (!isAnimating()) { |
| 652 | mActivity.onAnimationEnd(this); |
| 653 | } |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 654 | } |
| 655 | |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 656 | private void updateAnimatingConversationItems(Object obj, HashSet<Long> items) { |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 657 | if (!items.isEmpty()) { |
| 658 | if (obj instanceof ConversationItemView) { |
| 659 | final ConversationItemView target = (ConversationItemView) obj; |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 660 | final long id = target.getConversation().id; |
| 661 | items.remove(id); |
| 662 | mAnimatingViews.remove(id); |
Mindy Pereira | 067ef97 | 2012-08-08 14:04:06 -0700 | [diff] [blame] | 663 | if (items.isEmpty()) { |
| 664 | performAndSetNextAction(null); |
| 665 | notifyDataSetChanged(); |
| 666 | } |
| 667 | } |
| 668 | } |
Mindy Pereira | bdb43d5 | 2012-07-25 17:46:00 -0700 | [diff] [blame] | 669 | } |
| 670 | |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 671 | @Override |
Mindy Pereira | 866d319 | 2012-03-26 09:50:00 -0700 | [diff] [blame] | 672 | public boolean areAllItemsEnabled() { |
Vikram Aggarwal | b54a467 | 2012-04-11 11:32:44 -0700 | [diff] [blame] | 673 | // The animating positions are not enabled. |
Mindy Pereira | 866d319 | 2012-03-26 09:50:00 -0700 | [diff] [blame] | 674 | return false; |
| 675 | } |
| 676 | |
| 677 | @Override |
| 678 | public boolean isEnabled(int position) { |
Mindy Pereira | a41baad | 2012-04-13 08:30:54 -0700 | [diff] [blame] | 679 | return !isPositionDeleting(position) && !isPositionUndoing(position); |
Mindy Pereira | 866d319 | 2012-03-26 09:50:00 -0700 | [diff] [blame] | 680 | } |
| 681 | |
| 682 | @Override |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 683 | public void onAnimationCancel(Animator animation) { |
Mindy Pereira | 92cabad | 2012-01-31 12:53:22 -0800 | [diff] [blame] | 684 | onAnimationEnd(animation); |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 685 | } |
| 686 | |
| 687 | @Override |
| 688 | public void onAnimationRepeat(Animator animation) { |
Mindy Pereira | 9879d4c | 2012-01-31 09:34:18 -0800 | [diff] [blame] | 689 | } |
Mindy Pereira | 674afa4 | 2012-02-17 14:05:24 -0800 | [diff] [blame] | 690 | |
Mindy Pereira | 6681f6b | 2012-03-09 13:55:54 -0800 | [diff] [blame] | 691 | public void showFooter() { |
Paul Westbrook | 573b9e6 | 2012-08-02 14:56:17 -0700 | [diff] [blame] | 692 | setFooterVisibility(true); |
Mindy Pereira | 6681f6b | 2012-03-09 13:55:54 -0800 | [diff] [blame] | 693 | } |
| 694 | |
| 695 | public void hideFooter() { |
Paul Westbrook | 573b9e6 | 2012-08-02 14:56:17 -0700 | [diff] [blame] | 696 | setFooterVisibility(false); |
| 697 | } |
| 698 | |
| 699 | public void setFooterVisibility(boolean show) { |
| 700 | if (mShowFooter != show) { |
| 701 | mShowFooter = show; |
Mindy Pereira | 6681f6b | 2012-03-09 13:55:54 -0800 | [diff] [blame] | 702 | notifyDataSetChanged(); |
| 703 | } |
| 704 | } |
| 705 | |
| 706 | public void addFooter(View footerView) { |
| 707 | mFooter = footerView; |
| 708 | } |
Mindy Pereira | 4584a0d | 2012-03-13 14:42:14 -0700 | [diff] [blame] | 709 | |
| 710 | public void setFolder(Folder folder) { |
| 711 | mFolder = folder; |
| 712 | } |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 713 | |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 714 | public void clearLeaveBehind(long itemId) { |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 715 | if (hasLeaveBehinds() && mLeaveBehindItems.containsKey(itemId)) { |
| 716 | mLeaveBehindItems.remove(itemId); |
Mindy Pereira | 8f39702 | 2012-08-12 11:19:15 -0700 | [diff] [blame] | 717 | } else if (hasFadeLeaveBehinds()) { |
| 718 | mFadeLeaveBehindItems.remove(itemId); |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 719 | } else { |
| 720 | LogUtils.d(LOG_TAG, "Trying to clear a non-existant leave behind"); |
| 721 | } |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 722 | if (mLastLeaveBehind == itemId) { |
| 723 | mLastLeaveBehind = -1; |
| 724 | } |
Mindy Pereira | 6c72a78 | 2012-04-07 14:29:15 -0700 | [diff] [blame] | 725 | } |
Vikram Aggarwal | 7d81600 | 2012-04-17 17:06:41 -0700 | [diff] [blame] | 726 | |
Mindy Pereira | d3367499 | 2012-06-25 16:26:30 -0700 | [diff] [blame] | 727 | public void onSaveInstanceState(Bundle outState) { |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 728 | long[] lastDeleting = new long[mLastDeletingItems.size()]; |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 729 | for (int i = 0; i < lastDeleting.length; i++) { |
Mindy Pereira | d3367499 | 2012-06-25 16:26:30 -0700 | [diff] [blame] | 730 | lastDeleting[i] = mLastDeletingItems.get(i); |
| 731 | } |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 732 | outState.putLongArray(LAST_DELETING_ITEMS, lastDeleting); |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 733 | if (hasLeaveBehinds()) { |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 734 | if (mLastLeaveBehind != -1) { |
| 735 | outState.putParcelable(LEAVE_BEHIND_ITEM_DATA, |
| 736 | mLeaveBehindItems.get(mLastLeaveBehind).getLeaveBehindData()); |
| 737 | outState.putLong(LEAVE_BEHIND_ITEM_ID, mLastLeaveBehind); |
| 738 | } |
| 739 | for (LeaveBehindItem item : mLeaveBehindItems.values()) { |
| 740 | if (mLastLeaveBehind == -1 || item.getData().id != mLastLeaveBehind) { |
| 741 | item.commit(); |
| 742 | } |
| 743 | } |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 744 | } |
Mindy Pereira | d3367499 | 2012-06-25 16:26:30 -0700 | [diff] [blame] | 745 | } |
| 746 | |
Mindy Pereira | d3367499 | 2012-06-25 16:26:30 -0700 | [diff] [blame] | 747 | public void onRestoreInstanceState(Bundle outState) { |
Paul Westbrook | b8ef4ac | 2012-07-16 16:56:14 -0700 | [diff] [blame] | 748 | if (outState.containsKey(LAST_DELETING_ITEMS)) { |
mindyp | e85b8cd | 2012-08-23 11:32:26 -0700 | [diff] [blame] | 749 | final long[] lastDeleting = outState.getLongArray(LAST_DELETING_ITEMS); |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 750 | for (int i = 0; i < lastDeleting.length; i++) { |
Paul Westbrook | b8ef4ac | 2012-07-16 16:56:14 -0700 | [diff] [blame] | 751 | mLastDeletingItems.add(lastDeleting[i]); |
| 752 | } |
Mindy Pereira | d3367499 | 2012-06-25 16:26:30 -0700 | [diff] [blame] | 753 | } |
mindyp | 6a6deb2 | 2012-11-26 11:40:15 -0800 | [diff] [blame] | 754 | if (outState.containsKey(LEAVE_BEHIND_ITEM_DATA)) { |
| 755 | LeaveBehindData left = |
| 756 | (LeaveBehindData) outState.getParcelable(LEAVE_BEHIND_ITEM_DATA); |
| 757 | mLeaveBehindItems.put(outState.getLong(LEAVE_BEHIND_ITEM_ID), |
| 758 | setupLeaveBehind(left.data, left.op, left.data.position)); |
Mindy Pereira | 79e6a08 | 2012-07-25 13:36:53 -0700 | [diff] [blame] | 759 | } |
Mindy Pereira | d3367499 | 2012-06-25 16:26:30 -0700 | [diff] [blame] | 760 | } |
Mindy Pereira | 69e88dd | 2012-08-10 09:30:18 -0700 | [diff] [blame] | 761 | |
| 762 | /** |
| 763 | * Return if the adapter is in the process of animating anything. |
| 764 | */ |
| 765 | public boolean isAnimating() { |
| 766 | return !mUndoingItems.isEmpty() |
| 767 | || !mSwipeUndoingItems.isEmpty() |
Mindy Pereira | 1e946db | 2012-08-12 12:05:06 -0700 | [diff] [blame] | 768 | || hasFadeLeaveBehinds() |
Mindy Pereira | 69e88dd | 2012-08-10 09:30:18 -0700 | [diff] [blame] | 769 | || !mDeletingItems.isEmpty() |
| 770 | || !mSwipeDeletingItems.isEmpty(); |
| 771 | } |
mindyp | 215d2a4 | 2012-08-27 17:26:28 -0700 | [diff] [blame] | 772 | |
| 773 | /** |
| 774 | * Get the ConversationCursor associated with this adapter. |
| 775 | */ |
| 776 | public ConversationCursor getConversationCursor() { |
| 777 | return (ConversationCursor) getCursor(); |
| 778 | } |
mindyp | 7af43c9 | 2012-12-19 11:52:17 -0800 | [diff] [blame] | 779 | |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 780 | /** |
| 781 | * Get the currently visible leave behind item. |
| 782 | */ |
mindyp | 7af43c9 | 2012-12-19 11:52:17 -0800 | [diff] [blame] | 783 | public LeaveBehindItem getLastLeaveBehindItem() { |
| 784 | if (mLastLeaveBehind != -1) { |
| 785 | return mLeaveBehindItems.get(mLastLeaveBehind); |
| 786 | } |
| 787 | return null; |
| 788 | } |
mindyp | 4d4531a | 2013-01-16 15:39:53 -0800 | [diff] [blame] | 789 | |
| 790 | /** |
| 791 | * Cancel fading out the text displayed in the leave behind item currently |
| 792 | * shown. |
| 793 | */ |
| 794 | public void cancelFadeOutLastLeaveBehindItemText() { |
| 795 | LeaveBehindItem item = getLastLeaveBehindItem(); |
| 796 | if (item != null) { |
| 797 | item.cancelFadeOutText(); |
| 798 | } |
| 799 | } |
Paul Westbrook | 7ed5377 | 2013-01-23 10:19:55 -0800 | [diff] [blame^] | 800 | } |