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