SwipeableListView.java revision 512821c11d89d49908f3cfdee0b582601f500f3d
1f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira/*
2f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * Copyright (C) 2012 Google Inc.
3f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * Licensed to The Android Open Source Project.
4f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira *
5f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
6f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * you may not use this file except in compliance with the License.
7f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * You may obtain a copy of the License at
8f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira *
9f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
10f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira *
11f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * Unless required by applicable law or agreed to in writing, software
12f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
13f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * See the License for the specific language governing permissions and
15f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira * limitations under the License.
16f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira */
17f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
18f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereirapackage com.android.mail.ui;
19f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
20b2033d855ab0f13e253e5403ce25989bcbc49488Andy Huangimport android.content.ContentValues;
21f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.content.Context;
22f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereiraimport android.content.res.Configuration;
237517e3b61b898a57f19be0671f70d58a82224643Andy Huangimport android.graphics.Rect;
248db7e407109532557718c6b8064792f2df7a073dMindy Pereiraimport android.net.Uri;
25f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.util.AttributeSet;
26f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.view.MotionEvent;
27f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.view.View;
28f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereiraimport android.view.ViewConfiguration;
297c411aafd9ef059f881b7532426e61d1def54a07Andrew Sappersteinimport android.widget.AbsListView;
307c411aafd9ef059f881b7532426e61d1def54a07Andrew Sappersteinimport android.widget.AbsListView.OnScrollListener;
31af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Caoimport android.widget.HeaderViewListAdapter;
32f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.widget.ListView;
33f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
34f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.R;
35042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huangimport com.android.mail.analytics.Analytics;
36bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrookimport com.android.mail.browse.ConversationCursor;
37f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.browse.ConversationItemView;
381ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereiraimport com.android.mail.browse.SwipeableConversationItemView;
396126d72ae2769bd39451872f45781cadb5b90515Mark Weiimport com.android.mail.providers.Account;
40f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.providers.Conversation;
4106642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereiraimport com.android.mail.providers.Folder;
42b2033d855ab0f13e253e5403ce25989bcbc49488Andy Huangimport com.android.mail.providers.FolderList;
43f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.ui.SwipeHelper.Callback;
44b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
45b0219eb7b77212902a38821c050922ac73a68250Mindy Pereiraimport com.android.mail.utils.LogUtils;
467517e3b61b898a57f19be0671f70d58a82224643Andy Huangimport com.android.mail.utils.Utils;
47f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
48866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereiraimport java.util.ArrayList;
49f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport java.util.Collection;
508db7e407109532557718c6b8064792f2df7a073dMindy Pereiraimport java.util.HashMap;
51f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
5269b440141f402669fe44dfd8924a94fd22ebccf7mindyppublic class SwipeableListView extends ListView implements Callback, OnScrollListener {
536126d72ae2769bd39451872f45781cadb5b90515Mark Wei    private final SwipeHelper mSwipeHelper;
54ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    private boolean mEnableSwipe = false;
55489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang
56b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    public static final String LOG_TAG = LogTag.getLogTag();
5752a61db87c487fe5bb7cc673037887a6d35b0f0fMark Wei
58866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    private ConversationSelectionSet mConvSelectionSet;
596c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    private int mSwipeAction;
606126d72ae2769bd39451872f45781cadb5b90515Mark Wei    private Account mAccount;
6106642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira    private Folder mFolder;
629365a826b46b0e274df88e92534f7d871eef2aa2mindyp    private ListItemSwipedListener mSwipedListener;
6369b440141f402669fe44dfd8924a94fd22ebccf7mindyp    private boolean mScrolling;
64866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
65e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    private SwipeListener mSwipeListener;
66e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
674485ade4b8c949a222f8b98650a9a48d074dc87eVikram Aggarwal    // Instantiated through view inflation
684485ade4b8c949a222f8b98650a9a48d074dc87eVikram Aggarwal    @SuppressWarnings("unused")
69f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public SwipeableListView(Context context) {
70f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        this(context, null);
71f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
72f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
73f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public SwipeableListView(Context context, AttributeSet attrs) {
74f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        this(context, attrs, -1);
75f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
76f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
77f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public SwipeableListView(Context context, AttributeSet attrs, int defStyle) {
78f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        super(context, attrs, defStyle);
792229eddfba6f7517c43ef49ee67c50c24d4b961aMark Wei        setOnScrollListener(this);
80f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        float densityScale = getResources().getDisplayMetrics().density;
81f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        float pagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
826f0b2be9d3855e93ec2977b2736798c60adb9b7eMindy Pereira        mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale,
836f0b2be9d3855e93ec2977b2736798c60adb9b7eMindy Pereira                pagingTouchSlop);
84f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
85f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
86f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira    @Override
87f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira    protected void onConfigurationChanged(Configuration newConfig) {
88f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        super.onConfigurationChanged(newConfig);
89f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        float densityScale = getResources().getDisplayMetrics().density;
90f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        mSwipeHelper.setDensityScale(densityScale);
91f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
92f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
93f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira    }
94f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira
957517e3b61b898a57f19be0671f70d58a82224643Andy Huang    @Override
967517e3b61b898a57f19be0671f70d58a82224643Andy Huang    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
97bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal        LogUtils.d(Utils.VIEW_DEBUGGING_TAG,
987517e3b61b898a57f19be0671f70d58a82224643Andy Huang                "START CLF-ListView.onFocusChanged layoutRequested=%s root.layoutRequested=%s",
997517e3b61b898a57f19be0671f70d58a82224643Andy Huang                isLayoutRequested(), getRootView().isLayoutRequested());
1007517e3b61b898a57f19be0671f70d58a82224643Andy Huang        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
101bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal        LogUtils.d(Utils.VIEW_DEBUGGING_TAG, new Error(),
1027517e3b61b898a57f19be0671f70d58a82224643Andy Huang                "FINISH CLF-ListView.onFocusChanged layoutRequested=%s root.layoutRequested=%s",
1037517e3b61b898a57f19be0671f70d58a82224643Andy Huang                isLayoutRequested(), getRootView().isLayoutRequested());
1047517e3b61b898a57f19be0671f70d58a82224643Andy Huang    }
1057517e3b61b898a57f19be0671f70d58a82224643Andy Huang
106ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    /**
107ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira     * Enable swipe gestures.
108ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira     */
109ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    public void enableSwipe(boolean enable) {
110ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira        mEnableSwipe = enable;
111ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    }
112ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira
1136c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public void setSwipeAction(int action) {
1146c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        mSwipeAction = action;
115f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
116f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
1177c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein    public void setListItemSwipedListener(ListItemSwipedListener listener) {
1189365a826b46b0e274df88e92534f7d871eef2aa2mindyp        mSwipedListener = listener;
1199365a826b46b0e274df88e92534f7d871eef2aa2mindyp    }
1209365a826b46b0e274df88e92534f7d871eef2aa2mindyp
121de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    public int getSwipeAction() {
122de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira        return mSwipeAction;
123de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    }
124de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira
125866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    public void setSelectionSet(ConversationSelectionSet set) {
126866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira        mConvSelectionSet = set;
127866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    }
128866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
1296126d72ae2769bd39451872f45781cadb5b90515Mark Wei    public void setCurrentAccount(Account account) {
1306126d72ae2769bd39451872f45781cadb5b90515Mark Wei        mAccount = account;
1316126d72ae2769bd39451872f45781cadb5b90515Mark Wei    }
1326126d72ae2769bd39451872f45781cadb5b90515Mark Wei
13306642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira    public void setCurrentFolder(Folder folder) {
13406642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira        mFolder = folder;
13506642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira    }
13606642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira
137866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    @Override
138866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    public ConversationSelectionSet getSelectionSet() {
139866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira        return mConvSelectionSet;
140866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    }
141866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
142f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
143f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public boolean onInterceptTouchEvent(MotionEvent ev) {
1443432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao        if (mScrolling) {
14519bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira            return super.onInterceptTouchEvent(ev);
14669b440141f402669fe44dfd8924a94fd22ebccf7mindyp        } else {
14769b440141f402669fe44dfd8924a94fd22ebccf7mindyp            return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
14819bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira        }
149f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
150f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
151f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
152f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public boolean onTouchEvent(MotionEvent ev) {
1533432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao        return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
154f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
155f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
156f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
157f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public View getChildAtPosition(MotionEvent ev) {
158f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        // find the view under the pointer, accounting for GONE views
159f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        final int count = getChildCount();
160f69d0394f7de5c0681b8d5d5ca0c60b55c55f815Vikram Aggarwal        final int touchY = (int) ev.getY();
161f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        int childIdx = 0;
162f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        View slidingChild;
163f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        for (; childIdx < count; childIdx++) {
164f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira            slidingChild = getChildAt(childIdx);
165f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira            if (slidingChild.getVisibility() == GONE) {
166f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira                continue;
167f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira            }
168866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira            if (touchY >= slidingChild.getTop() && touchY <= slidingChild.getBottom()) {
1691ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereira                if (slidingChild instanceof SwipeableConversationItemView) {
1701ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereira                    return ((SwipeableConversationItemView) slidingChild).getSwipeableItemView();
1711ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereira                }
172866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira                return slidingChild;
173866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira            }
174f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        }
175f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        return null;
176f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
177f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
178f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
1796c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public boolean canChildBeDismissed(SwipeableItemView v) {
1803432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao        return mEnableSwipe && v.canChildBeDismissed();
181f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
182f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
183f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
1846c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public void onChildDismissed(SwipeableItemView v) {
1850760bfbf8259e4161ce7737fc980b6b9297885ccmindyp        if (v != null) {
1860760bfbf8259e4161ce7737fc980b6b9297885ccmindyp            v.dismiss();
1870760bfbf8259e4161ce7737fc980b6b9297885ccmindyp        }
188f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
189f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
1908937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    // Call this whenever a new action is taken; this forces a commit of any
1918937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    // existing destructive actions.
192c6adce3cf6887c4c6dd5005724565702751843d0mindyp    public void commitDestructiveActions(boolean animate) {
1930fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira        final AnimatedAdapter adapter = getAnimatedAdapter();
1948937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira        if (adapter != null) {
195c6adce3cf6887c4c6dd5005724565702751843d0mindyp            adapter.commitLeaveBehindItems(animate);
1968937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira        }
1978937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    }
1988937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira
199a538984fcc19e7624f2650b119ede39bf1f35846mindyp    public void dismissChild(final ConversationItemView target) {
2007c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        // Notifies the SwipeListener that a swipe has ended.
2017c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        if (mSwipeListener != null) {
2027c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein            mSwipeListener.onEndSwipe();
2037c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        }
2047c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein
205d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        final ToastBarOperation undoOp;
206866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
2076a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy        undoOp = new ToastBarOperation(1, mSwipeAction, ToastBarOperation.UNDO, false /* batch */,
2086a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                mFolder);
2096c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        Conversation conv = target.getConversation();
21013e6243683cc8cb998a43d06f25d6ff1aab9d93bMindy Pereira        target.getConversation().position = findConversation(target, conv);
2110fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira        final AnimatedAdapter adapter = getAnimatedAdapter();
21209f1ae9a9f209436c0c6b44a6e76b32b27937a01Mindy Pereira        if (adapter == null) {
21309f1ae9a9f209436c0c6b44a6e76b32b27937a01Mindy Pereira            return;
21409f1ae9a9f209436c0c6b44a6e76b32b27937a01Mindy Pereira        }
215370f868c834861e7732faaa9bdd07a0fa0105596Andy Huang        adapter.setupLeaveBehind(conv, undoOp, conv.position, target.getHeight());
21600ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira        ConversationCursor cc = (ConversationCursor) adapter.getCursor();
2179365a826b46b0e274df88e92534f7d871eef2aa2mindyp        Collection<Conversation> convList = Conversation.listOf(conv);
2185cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp        ArrayList<Uri> folderUris;
2195cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp        ArrayList<Boolean> adds;
220042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang
2212b55549d4fbe25e91f673a0727fc89ff755d9327Andy Huang        Analytics.getInstance().sendMenuItemEvent("list_swipe", mSwipeAction, null, 0);
222042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang
2232b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        if (mSwipeAction == R.id.remove_folder) {
2242b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            FolderOperation folderOp = new FolderOperation(mFolder, false);
2252b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            HashMap<Uri, Folder> targetFolders = Folder
2262b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    .hashMapForFolders(conv.getRawFolders());
2272b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            targetFolders.remove(folderOp.mFolder.folderUri.fullUri);
2282b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            final FolderList folders = FolderList.copyOf(targetFolders.values());
2292b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            conv.setRawFolders(folders);
2302b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            final ContentValues values = new ContentValues();
2312b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            folderUris = new ArrayList<Uri>();
2322b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            folderUris.add(mFolder.folderUri.fullUri);
2332b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            adds = new ArrayList<Boolean>();
2342b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            adds.add(Boolean.FALSE);
2352b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            ConversationCursor.addFolderUpdates(folderUris, adds, values);
2362b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            ConversationCursor.addTargetFolders(targetFolders.values(), values);
2372b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            cc.mostlyDestructiveUpdate(Conversation.listOf(conv), values);
2382b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (mSwipeAction == R.id.archive) {
2392b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            cc.mostlyArchive(convList);
2402b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (mSwipeAction == R.id.delete) {
2412b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            cc.mostlyDelete(convList);
242512821c11d89d49908f3cfdee0b582601f500f3dJin Cao        } else if (mSwipeAction == R.id.discard_outbox) {
243512821c11d89d49908f3cfdee0b582601f500f3dJin Cao            cc.moveFailedIntoDrafts(convList);
2446c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        }
2459365a826b46b0e274df88e92534f7d871eef2aa2mindyp        if (mSwipedListener != null) {
2469365a826b46b0e274df88e92534f7d871eef2aa2mindyp            mSwipedListener.onListItemSwiped(convList);
2479365a826b46b0e274df88e92534f7d871eef2aa2mindyp        }
2486c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        adapter.notifyDataSetChanged();
249a8ead90ce1e6c66e4ecacdf7cfa25c2cafc9bb3bMindy Pereira        if (mConvSelectionSet != null && !mConvSelectionSet.isEmpty()
250a8ead90ce1e6c66e4ecacdf7cfa25c2cafc9bb3bMindy Pereira                && mConvSelectionSet.contains(conv)) {
25120a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal            mConvSelectionSet.toggle(conv);
25254f120f6ada40c7191811dfff99b151e9e192c78mindyp            // Don't commit destructive actions if the item we just removed from
25354f120f6ada40c7191811dfff99b151e9e192c78mindyp            // the selection set is the item we just destroyed!
25454f120f6ada40c7191811dfff99b151e9e192c78mindyp            if (!conv.isMostlyDead() && mConvSelectionSet.isEmpty()) {
25554f120f6ada40c7191811dfff99b151e9e192c78mindyp                commitDestructiveActions(true);
25654f120f6ada40c7191811dfff99b151e9e192c78mindyp            }
257cff4a2b2e92258c6c85ed4af15bc13101aa71170Mindy Pereira        }
258f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
259f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
260f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
261f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public void onBeginDrag(View v) {
262f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        // We do this so the underlying ScrollView knows that it won't get
263f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        // the chance to intercept events anymore
264f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        requestDisallowInterceptTouchEvent(true);
2653b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        cancelDismissCounter();
266e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
2677c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        // Notifies the SwipeListener that a swipe has begun.
268e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        if (mSwipeListener != null) {
269e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang            mSwipeListener.onBeginSwipe();
270e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        }
271f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
272f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
273f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
2746c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public void onDragCancelled(SwipeableItemView v) {
2753b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        final AnimatedAdapter adapter = getAnimatedAdapter();
2763b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        if (adapter != null) {
2773b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp            adapter.startDismissCounter();
2784d4531a63cff536d2ee1a2929d0820981df8516amindyp            adapter.cancelFadeOutLastLeaveBehindItemText();
2793b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        }
2807c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein
2817c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        // Notifies the SwipeListener that a swipe has ended.
2827c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        if (mSwipeListener != null) {
2837c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein            mSwipeListener.onEndSwipe();
2847c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        }
285f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
286f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
28707118a01f7183645957010779222b84930f75b4eMindy Pereira    /**
28807118a01f7183645957010779222b84930f75b4eMindy Pereira     * Archive items using the swipe away animation before shrinking them away.
28907118a01f7183645957010779222b84930f75b4eMindy Pereira     */
290cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook    public boolean destroyItems(Collection<Conversation> convs,
29184f7d32bdc79263004ed5241480988e02f8e618cmindyp            final ListItemsRemovedListener listener) {
29284f7d32bdc79263004ed5241480988e02f8e618cmindyp        if (convs == null) {
293cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook            LogUtils.e(LOG_TAG, "SwipeableListView.destroyItems: null conversations.");
294cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook            return false;
29584f7d32bdc79263004ed5241480988e02f8e618cmindyp        }
296a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal        final AnimatedAdapter adapter = getAnimatedAdapter();
297a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal        if (adapter == null) {
298a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal            LogUtils.e(LOG_TAG, "SwipeableListView.destroyItems: Cannot destroy: adapter is null.");
299cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook            return false;
300067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        }
30184f7d32bdc79263004ed5241480988e02f8e618cmindyp        adapter.swipeDelete(convs, listener);
302cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook        return true;
303067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira    }
304067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira
305cc2f9296ad63ed681c200feb118c5caace36e72emindyp    public int findConversation(ConversationItemView view, Conversation conv) {
306dbb587f15c723ae80edb33f65c29cc2b6c15eba0Scott Kennedy        int position = INVALID_POSITION;
307067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        long convId = conv.id;
308067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        try {
309dbb587f15c723ae80edb33f65c29cc2b6c15eba0Scott Kennedy            position = getPositionForView(view);
310067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        } catch (Exception e) {
311067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            position = INVALID_POSITION;
312599e7f8bf95d2f21a966cbff1bf72adf77a90a33Andy Huang            LogUtils.w(LOG_TAG, e, "Exception finding position; using alternate strategy");
313067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        }
314067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        if (position == INVALID_POSITION) {
315067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            // Try the other way!
316067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            Conversation foundConv;
317067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            long foundId;
318067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            for (int i = 0; i < getChildCount(); i++) {
319067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                View child = getChildAt(i);
320067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                if (child instanceof SwipeableConversationItemView) {
321067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    foundConv = ((SwipeableConversationItemView) child).getSwipeableItemView()
322067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                            .getConversation();
323067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    foundId = foundConv.id;
324067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    if (foundId == convId) {
325599e7f8bf95d2f21a966cbff1bf72adf77a90a33Andy Huang                        position = i + getFirstVisiblePosition();
326067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                        break;
327067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    }
328067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                }
329067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            }
330c79aec78d06928b3fa1464d6aed60019f9b4843bMindy Pereira        }
331067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        return position;
33207118a01f7183645957010779222b84930f75b4eMindy Pereira    }
33307118a01f7183645957010779222b84930f75b4eMindy Pereira
3340fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira    private AnimatedAdapter getAnimatedAdapter() {
3350fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira        return (AnimatedAdapter) getAdapter();
3360fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira    }
3370fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira
3388937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    @Override
3398937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    public boolean performItemClick(View view, int pos, long id) {
340f69d0394f7de5c0681b8d5d5ca0c60b55c55f815Vikram Aggarwal        final int previousPosition = getCheckedItemPosition();
341f69d0394f7de5c0681b8d5d5ca0c60b55c55f815Vikram Aggarwal        final boolean selectionSetEmpty = mConvSelectionSet.isEmpty();
3426126d72ae2769bd39451872f45781cadb5b90515Mark Wei
3436126d72ae2769bd39451872f45781cadb5b90515Mark Wei        // Superclass method modifies the selection set
344f69d0394f7de5c0681b8d5d5ca0c60b55c55f815Vikram Aggarwal        final boolean handled = super.performItemClick(view, pos, id);
3456126d72ae2769bd39451872f45781cadb5b90515Mark Wei
3461fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy        // If we are in CAB mode then a click shouldn't
3476126d72ae2769bd39451872f45781cadb5b90515Mark Wei        // activate the new item, it should only add it to the selection set
3481fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy        if (!selectionSetEmpty && previousPosition != -1) {
3496126d72ae2769bd39451872f45781cadb5b90515Mark Wei            setItemChecked(previousPosition, true);
3506126d72ae2769bd39451872f45781cadb5b90515Mark Wei        }
35100ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira        // Commit any existing destructive actions when the user selects a
35200ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira        // conversation to view.
353c6adce3cf6887c4c6dd5005724565702751843d0mindyp        commitDestructiveActions(true);
354dd9d3a16148b178cb95fd4db4168a67c64eb944fMindy Pereira        return handled;
3558937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    }
3565254486a8c37cdf2b579aa7a3b9c8eb7324062bcmindyp
3575254486a8c37cdf2b579aa7a3b9c8eb7324062bcmindyp    @Override
358d064972c4a032f3c5cfcb236aa470273f7ac2ce0mindyp    public void onScroll() {
359d064972c4a032f3c5cfcb236aa470273f7ac2ce0mindyp        commitDestructiveActions(true);
3605254486a8c37cdf2b579aa7a3b9c8eb7324062bcmindyp    }
3619365a826b46b0e274df88e92534f7d871eef2aa2mindyp
3629365a826b46b0e274df88e92534f7d871eef2aa2mindyp    public interface ListItemsRemovedListener {
3639365a826b46b0e274df88e92534f7d871eef2aa2mindyp        public void onListItemsRemoved();
3649365a826b46b0e274df88e92534f7d871eef2aa2mindyp    }
3659365a826b46b0e274df88e92534f7d871eef2aa2mindyp
3669365a826b46b0e274df88e92534f7d871eef2aa2mindyp    public interface ListItemSwipedListener {
3679365a826b46b0e274df88e92534f7d871eef2aa2mindyp        public void onListItemSwiped(Collection<Conversation> conversations);
3689365a826b46b0e274df88e92534f7d871eef2aa2mindyp    }
36969b440141f402669fe44dfd8924a94fd22ebccf7mindyp
37069b440141f402669fe44dfd8924a94fd22ebccf7mindyp    @Override
37179c3e1ed9cb1e1660c5d34b7bf3f3ab5c5dd5162Andy Huang    public final void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
372479505d71969e26b0785d8e0e1b81108731cf827Mark Wei            int totalItemCount) {
37369b440141f402669fe44dfd8924a94fd22ebccf7mindyp    }
37469b440141f402669fe44dfd8924a94fd22ebccf7mindyp
37569b440141f402669fe44dfd8924a94fd22ebccf7mindyp    @Override
37681aea35d45b3d0191ec595562a2fcf67009845d5Mark Wei    public void onScrollStateChanged(final AbsListView view, final int scrollState) {
377479505d71969e26b0785d8e0e1b81108731cf827Mark Wei        mScrolling = scrollState != OnScrollListener.SCROLL_STATE_IDLE;
378479505d71969e26b0785d8e0e1b81108731cf827Mark Wei
37948ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang        if (!mScrolling) {
38048ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            final Context c = getContext();
38148ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            if (c instanceof ControllableActivity) {
38248ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang                final ControllableActivity activity = (ControllableActivity) c;
38348ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang                activity.onAnimationEnd(null /* adapter */);
38448ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            } else {
38548ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang                LogUtils.wtf(LOG_TAG, "unexpected context=%s", c);
38648ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            }
38748ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang        }
38848ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang    }
38948ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang
39048ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang    public boolean isScrolling() {
39148ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang        return mScrolling;
39269b440141f402669fe44dfd8924a94fd22ebccf7mindyp    }
3933b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp
3943b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp    @Override
3953b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp    public void cancelDismissCounter() {
3963b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        AnimatedAdapter adapter = getAnimatedAdapter();
3973b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        if (adapter != null) {
3983b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp            adapter.cancelDismissCounter();
3993b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        }
4003b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp    }
4017af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp
4027af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp    @Override
4037af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp    public LeaveBehindItem getLastSwipedItem() {
4047af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        AnimatedAdapter adapter = getAnimatedAdapter();
4057af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        if (adapter != null) {
4067af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp            return adapter.getLastLeaveBehindItem();
4077af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        }
4087af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        return null;
4097af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp    }
410e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
411e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    public void setSwipeListener(SwipeListener swipeListener) {
412e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        mSwipeListener = swipeListener;
413e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    }
414e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
415e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    public interface SwipeListener {
416e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        public void onBeginSwipe();
4177c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        public void onEndSwipe();
418e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    }
41900ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira}
420