AnimatedAdapter.java revision 4584a0d83e160444f931cb565185a2eea39b1683
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 18package com.android.mail.ui; 19 20import android.animation.Animator; 21import android.animation.ObjectAnimator; 22import android.content.Context; 23import android.database.Cursor; 24import android.view.View; 25import android.view.ViewGroup; 26import android.widget.AdapterView; 27import android.widget.SimpleCursorAdapter; 28 29import com.android.mail.browse.ConversationCursor; 30import com.android.mail.browse.ConversationItemView; 31import com.android.mail.browse.ConversationListFooterView; 32import com.android.mail.providers.Account; 33import com.android.mail.providers.Conversation; 34import com.android.mail.providers.Folder; 35import com.android.mail.providers.UIProvider; 36import com.android.mail.ui.UndoBarView.OnUndoCancelListener; 37 38import java.util.ArrayList; 39import java.util.Collection; 40import java.util.HashSet; 41 42public class AnimatedAdapter extends SimpleCursorAdapter implements 43 android.animation.Animator.AnimatorListener, OnUndoCancelListener { 44 private static int ITEM_VIEW_TYPE_FOOTER = 1; 45 private HashSet<Integer> mDeletingItems = new HashSet<Integer>(); 46 private Account mSelectedAccount; 47 private Context mContext; 48 private ConversationSelectionSet mBatchConversations; 49 private ActionCompleteListener mActionCompleteListener; 50 private boolean mUndo = false; 51 private ArrayList<Integer> mLastDeletingItems = new ArrayList<Integer>(); 52 private ViewMode mViewMode; 53 private View mFooter; 54 private boolean mShowFooter; 55 private Folder mFolder; 56 57 public AnimatedAdapter(Context context, int textViewResourceId, ConversationCursor cursor, 58 ConversationSelectionSet batch, Account account, ViewMode viewMode) { 59 super(context, textViewResourceId, cursor, UIProvider.CONVERSATION_PROJECTION, null, 0); 60 mContext = context; 61 mBatchConversations = batch; 62 mSelectedAccount = account; 63 mViewMode = viewMode; 64 mShowFooter = false; 65 } 66 67 @Override 68 public int getCount() { 69 int count = super.getCount(); 70 return mShowFooter? count + 1 : count; 71 } 72 73 public void setUndo(boolean state) { 74 mUndo = state; 75 if (mUndo) { 76 mDeletingItems.clear(); 77 mDeletingItems.addAll(mLastDeletingItems); 78 mActionCompleteListener = new ActionCompleteListener() { 79 @Override 80 public void onActionComplete() { 81 notifyDataSetChanged(); 82 }}; 83 } 84 } 85 86 @Override 87 public View newView(Context context, Cursor cursor, ViewGroup parent) { 88 ConversationItemView view = new ConversationItemView(context, mSelectedAccount.name); 89 return view; 90 } 91 92 @Override 93 public void bindView(View view, Context context, Cursor cursor) { 94 if (!isPositionAnimating(view) && !isPositionFooter(view)) { 95 ((ConversationItemView) view).bind(cursor, mSelectedAccount.name, mViewMode, 96 mBatchConversations, mFolder); 97 } 98 } 99 100 @Override 101 public boolean hasStableIds() { 102 return true; 103 } 104 105 @Override 106 public int getViewTypeCount() { 107 // Our normal view and the animating (not recycled) view 108 return 3; 109 } 110 111 @Override 112 public int getItemViewType(int position) { 113 // Don't recycle animating views 114 if (isPositionAnimating(position)) { 115 return AdapterView.ITEM_VIEW_TYPE_IGNORE; 116 } else if (mShowFooter && position == super.getCount()) { 117 return ITEM_VIEW_TYPE_FOOTER ; 118 } 119 return 0; 120 } 121 122 public void delete(Collection<Conversation> conversations, 123 ActionCompleteListener listener) { 124 // Animate out the positions. 125 // Call when all the animations are complete. 126 final ArrayList<Integer> positions = new ArrayList<Integer>(); 127 for (Conversation c : conversations) { 128 positions.add(c.position); 129 } 130 delete(positions, listener); 131 } 132 133 public void delete(ArrayList<Integer> deletedRows, ActionCompleteListener listener) { 134 // Clear out any remaining items and add the new ones 135 mLastDeletingItems.clear(); 136 mLastDeletingItems.addAll(deletedRows); 137 mDeletingItems.addAll(deletedRows); 138 mActionCompleteListener = listener; 139 // TODO(viki): Rather than notifying for a full data set change, perhaps we can mark 140 // only the affected conversations? 141 notifyDataSetChanged(); 142 } 143 144 @Override 145 public View getView(int position, View convertView, ViewGroup parent) { 146 if (mShowFooter && position == super.getCount()) { 147 return mFooter; 148 } 149 if (isPositionAnimating(position)) { 150 return getAnimatingView(position, convertView, parent); 151 } 152 return super.getView(position, convertView, parent); 153 } 154 155 /** 156 * Get an animating view. This happens when a list item is in the process of being removed 157 * from the list (items being deleted). 158 * @param position the position of the view inside the list 159 * @param convertView if null, a recycled view that we can reuse 160 * @param parent the parent view 161 * @return the view to show when animating an operation. 162 */ 163 private View getAnimatingView(int position, View convertView, ViewGroup parent) { 164 assert (convertView instanceof AnimatingItemView); 165 Conversation conversation = new Conversation((ConversationCursor) getItem(position)); 166 conversation.position = position; 167 final AnimatingItemView view = (convertView == null) ? new AnimatingItemView(mContext) 168 : (AnimatingItemView) convertView; 169 view.startAnimation(conversation, this, mUndo); 170 return view; 171 } 172 173 private boolean isPositionAnimating(int position) { 174 return mDeletingItems.contains(position); 175 } 176 177 private boolean isPositionAnimating(View view) { 178 return (view instanceof AnimatingItemView); 179 } 180 181 private boolean isPositionFooter(View view) { 182 return (view instanceof ConversationListFooterView); 183 } 184 185 @Override 186 public void onAnimationStart(Animator animation) { 187 // TODO Auto-generated method stub 188 } 189 190 @Override 191 public void onAnimationEnd(Animator animation) { 192 if (!mDeletingItems.isEmpty()) { 193 // See if we have received all the animations we expected; if so, 194 // call the listener and reset it. 195 int position = ((AnimatingItemView) 196 ((ObjectAnimator) animation).getTarget()).getData().position; 197 mDeletingItems.remove(position); 198 if (mDeletingItems.isEmpty()) { 199 if (mUndo) { 200 mLastDeletingItems.clear(); 201 mUndo = false; 202 } 203 if (mActionCompleteListener != null) { 204 mActionCompleteListener.onActionComplete(); 205 mActionCompleteListener = null; 206 } 207 } 208 } 209 } 210 211 @Override 212 public void onAnimationCancel(Animator animation) { 213 onAnimationEnd(animation); 214 } 215 216 @Override 217 public void onAnimationRepeat(Animator animation) { 218 // TODO Auto-generated method stub 219 } 220 221 @Override 222 public void onUndoCancel() { 223 mLastDeletingItems.clear(); 224 } 225 226 public void showFooter() { 227 if (!mShowFooter) { 228 mShowFooter = true; 229 notifyDataSetChanged(); 230 } 231 } 232 233 public void hideFooter() { 234 if (mShowFooter) { 235 mShowFooter = false; 236 notifyDataSetChanged(); 237 } 238 } 239 240 public void addFooter(View footerView) { 241 mFooter = footerView; 242 } 243 244 public void setFolder(Folder folder) { 245 mFolder = folder; 246 } 247} 248