SwipeableListView.java revision f9fd2cf057d845c7b081a3f17482dd0b886179d1
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;
31f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.widget.ListView;
32f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
33f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.R;
34042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huangimport com.android.mail.analytics.Analytics;
35bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrookimport com.android.mail.browse.ConversationCursor;
36f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.browse.ConversationItemView;
371ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereiraimport com.android.mail.browse.SwipeableConversationItemView;
386126d72ae2769bd39451872f45781cadb5b90515Mark Weiimport com.android.mail.providers.Account;
39f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.providers.Conversation;
4006642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereiraimport com.android.mail.providers.Folder;
41b2033d855ab0f13e253e5403ce25989bcbc49488Andy Huangimport com.android.mail.providers.FolderList;
42f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.ui.SwipeHelper.Callback;
43b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
44b0219eb7b77212902a38821c050922ac73a68250Mindy Pereiraimport com.android.mail.utils.LogUtils;
457517e3b61b898a57f19be0671f70d58a82224643Andy Huangimport com.android.mail.utils.Utils;
46f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
47866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereiraimport java.util.ArrayList;
48f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport java.util.Collection;
498db7e407109532557718c6b8064792f2df7a073dMindy Pereiraimport java.util.HashMap;
50f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
5169b440141f402669fe44dfd8924a94fd22ebccf7mindyppublic class SwipeableListView extends ListView implements Callback, OnScrollListener {
526126d72ae2769bd39451872f45781cadb5b90515Mark Wei    private final SwipeHelper mSwipeHelper;
53f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    /**
54f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     * Are swipes enabled on all items? (Each individual item can still prevent swiping.)<br>
55f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     * When swiping is disabled, the UI still reacts to the gesture to acknowledge it.
56f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     */
57ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    private boolean mEnableSwipe = false;
58f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    /**
59f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     * When set, we prevent the SwipeHelper from kicking in at all. This
60f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     * short-circuits {@link #mEnableSwipe}.
61f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     */
62f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    private boolean mPreventSwipesEntirely = false;
63489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang
64b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    public static final String LOG_TAG = LogTag.getLogTag();
6552a61db87c487fe5bb7cc673037887a6d35b0f0fMark Wei
66ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    private ConversationCheckedSet mConvCheckedSet;
676c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    private int mSwipeAction;
686126d72ae2769bd39451872f45781cadb5b90515Mark Wei    private Account mAccount;
6906642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira    private Folder mFolder;
709365a826b46b0e274df88e92534f7d871eef2aa2mindyp    private ListItemSwipedListener mSwipedListener;
7169b440141f402669fe44dfd8924a94fd22ebccf7mindyp    private boolean mScrolling;
72866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
73e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    private SwipeListener mSwipeListener;
74e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
754485ade4b8c949a222f8b98650a9a48d074dc87eVikram Aggarwal    // Instantiated through view inflation
764485ade4b8c949a222f8b98650a9a48d074dc87eVikram Aggarwal    @SuppressWarnings("unused")
77f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public SwipeableListView(Context context) {
78f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        this(context, null);
79f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
80f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
81f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public SwipeableListView(Context context, AttributeSet attrs) {
82f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        this(context, attrs, -1);
83f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
84f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
85f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public SwipeableListView(Context context, AttributeSet attrs, int defStyle) {
86f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        super(context, attrs, defStyle);
87f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        float densityScale = getResources().getDisplayMetrics().density;
88f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        float pagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
896f0b2be9d3855e93ec2977b2736798c60adb9b7eMindy Pereira        mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale,
906f0b2be9d3855e93ec2977b2736798c60adb9b7eMindy Pereira                pagingTouchSlop);
91f9fd2cf057d845c7b081a3f17482dd0b886179d1Rohan Shah        mScrolling = false;
92f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
93f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
94f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira    @Override
95f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira    protected void onConfigurationChanged(Configuration newConfig) {
96f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        super.onConfigurationChanged(newConfig);
97f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        float densityScale = getResources().getDisplayMetrics().density;
98f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        mSwipeHelper.setDensityScale(densityScale);
99f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
100f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
101f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira    }
102f913894d3f1fa80b7174477999074b813e1deb12Mindy Pereira
1037517e3b61b898a57f19be0671f70d58a82224643Andy Huang    @Override
1047517e3b61b898a57f19be0671f70d58a82224643Andy Huang    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
105bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal        LogUtils.d(Utils.VIEW_DEBUGGING_TAG,
1067517e3b61b898a57f19be0671f70d58a82224643Andy Huang                "START CLF-ListView.onFocusChanged layoutRequested=%s root.layoutRequested=%s",
1077517e3b61b898a57f19be0671f70d58a82224643Andy Huang                isLayoutRequested(), getRootView().isLayoutRequested());
1087517e3b61b898a57f19be0671f70d58a82224643Andy Huang        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
109bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal        LogUtils.d(Utils.VIEW_DEBUGGING_TAG, new Error(),
1107517e3b61b898a57f19be0671f70d58a82224643Andy Huang                "FINISH CLF-ListView.onFocusChanged layoutRequested=%s root.layoutRequested=%s",
1117517e3b61b898a57f19be0671f70d58a82224643Andy Huang                isLayoutRequested(), getRootView().isLayoutRequested());
1127517e3b61b898a57f19be0671f70d58a82224643Andy Huang    }
1137517e3b61b898a57f19be0671f70d58a82224643Andy Huang
114ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    /**
115ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira     * Enable swipe gestures.
116ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira     */
117ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    public void enableSwipe(boolean enable) {
118ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira        mEnableSwipe = enable;
119ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira    }
120ff9aff3c28c4c20c7866ebfaaa642997e324f274Mindy Pereira
121f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    /**
122f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     * Completely ignore any horizontal swiping gestures.
123f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     */
124f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    public void preventSwipesEntirely() {
125f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang        mPreventSwipesEntirely = true;
126f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    }
127f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang
128f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    /**
129f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     * Reverses a prior call to {@link #preventSwipesEntirely()}.
130f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang     */
131f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    public void stopPreventingSwipes() {
132f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang        mPreventSwipesEntirely = false;
133f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang    }
134f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang
1356c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public void setSwipeAction(int action) {
1366c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        mSwipeAction = action;
137f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
138f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
1397c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein    public void setListItemSwipedListener(ListItemSwipedListener listener) {
1409365a826b46b0e274df88e92534f7d871eef2aa2mindyp        mSwipedListener = listener;
1419365a826b46b0e274df88e92534f7d871eef2aa2mindyp    }
1429365a826b46b0e274df88e92534f7d871eef2aa2mindyp
143de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    public int getSwipeAction() {
144de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira        return mSwipeAction;
145de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    }
146de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira
147ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    public void setCheckedSet(ConversationCheckedSet set) {
148ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        mConvCheckedSet = set;
149866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    }
150866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
1516126d72ae2769bd39451872f45781cadb5b90515Mark Wei    public void setCurrentAccount(Account account) {
1526126d72ae2769bd39451872f45781cadb5b90515Mark Wei        mAccount = account;
1536126d72ae2769bd39451872f45781cadb5b90515Mark Wei    }
1546126d72ae2769bd39451872f45781cadb5b90515Mark Wei
15506642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira    public void setCurrentFolder(Folder folder) {
15606642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira        mFolder = folder;
15706642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira    }
15806642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira
159866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    @Override
160ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    public ConversationCheckedSet getCheckedSet() {
161ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        return mConvCheckedSet;
162866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira    }
163866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
164f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
165f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public boolean onInterceptTouchEvent(MotionEvent ev) {
1663432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao        if (mScrolling) {
16719bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira            return super.onInterceptTouchEvent(ev);
16869b440141f402669fe44dfd8924a94fd22ebccf7mindyp        } else {
169f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang            return (!mPreventSwipesEntirely && mSwipeHelper.onInterceptTouchEvent(ev))
170f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang                    || super.onInterceptTouchEvent(ev);
17119bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira        }
172f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
173f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
174f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
175f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public boolean onTouchEvent(MotionEvent ev) {
176f211b12a7cdf6556f489df51c6f57dd79e8887feAndy Huang        return (!mPreventSwipesEntirely && mSwipeHelper.onTouchEvent(ev)) || super.onTouchEvent(ev);
177f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
178f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
179f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
180f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public View getChildAtPosition(MotionEvent ev) {
181f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        // find the view under the pointer, accounting for GONE views
182f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        final int count = getChildCount();
183f69d0394f7de5c0681b8d5d5ca0c60b55c55f815Vikram Aggarwal        final int touchY = (int) ev.getY();
184f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        int childIdx = 0;
185f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        View slidingChild;
186f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        for (; childIdx < count; childIdx++) {
187f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira            slidingChild = getChildAt(childIdx);
188f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira            if (slidingChild.getVisibility() == GONE) {
189f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira                continue;
190f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira            }
191866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira            if (touchY >= slidingChild.getTop() && touchY <= slidingChild.getBottom()) {
1921ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereira                if (slidingChild instanceof SwipeableConversationItemView) {
1931ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereira                    return ((SwipeableConversationItemView) slidingChild).getSwipeableItemView();
1941ef988f0c8be136fda75ed207c222413db1d3f0cMindy Pereira                }
195866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira                return slidingChild;
196866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira            }
197f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        }
198f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        return null;
199f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
200f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
201f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
2026c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public boolean canChildBeDismissed(SwipeableItemView v) {
2033432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao        return mEnableSwipe && v.canChildBeDismissed();
204f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
205f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
206f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
2076c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public void onChildDismissed(SwipeableItemView v) {
2080760bfbf8259e4161ce7737fc980b6b9297885ccmindyp        if (v != null) {
2090760bfbf8259e4161ce7737fc980b6b9297885ccmindyp            v.dismiss();
2100760bfbf8259e4161ce7737fc980b6b9297885ccmindyp        }
211f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
212f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
2138937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    // Call this whenever a new action is taken; this forces a commit of any
2148937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    // existing destructive actions.
215c6adce3cf6887c4c6dd5005724565702751843d0mindyp    public void commitDestructiveActions(boolean animate) {
2160fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira        final AnimatedAdapter adapter = getAnimatedAdapter();
2178937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira        if (adapter != null) {
218c6adce3cf6887c4c6dd5005724565702751843d0mindyp            adapter.commitLeaveBehindItems(animate);
2198937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira        }
2208937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    }
2218937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira
222a538984fcc19e7624f2650b119ede39bf1f35846mindyp    public void dismissChild(final ConversationItemView target) {
2237c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        // Notifies the SwipeListener that a swipe has ended.
2247c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        if (mSwipeListener != null) {
2257c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein            mSwipeListener.onEndSwipe();
2267c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        }
2277c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein
228d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        final ToastBarOperation undoOp;
229866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira
2306a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy        undoOp = new ToastBarOperation(1, mSwipeAction, ToastBarOperation.UNDO, false /* batch */,
2316a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                mFolder);
2326c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        Conversation conv = target.getConversation();
23313e6243683cc8cb998a43d06f25d6ff1aab9d93bMindy Pereira        target.getConversation().position = findConversation(target, conv);
2340fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira        final AnimatedAdapter adapter = getAnimatedAdapter();
23509f1ae9a9f209436c0c6b44a6e76b32b27937a01Mindy Pereira        if (adapter == null) {
23609f1ae9a9f209436c0c6b44a6e76b32b27937a01Mindy Pereira            return;
23709f1ae9a9f209436c0c6b44a6e76b32b27937a01Mindy Pereira        }
238370f868c834861e7732faaa9bdd07a0fa0105596Andy Huang        adapter.setupLeaveBehind(conv, undoOp, conv.position, target.getHeight());
23900ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira        ConversationCursor cc = (ConversationCursor) adapter.getCursor();
2409365a826b46b0e274df88e92534f7d871eef2aa2mindyp        Collection<Conversation> convList = Conversation.listOf(conv);
2415cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp        ArrayList<Uri> folderUris;
2425cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp        ArrayList<Boolean> adds;
243042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang
2442b55549d4fbe25e91f673a0727fc89ff755d9327Andy Huang        Analytics.getInstance().sendMenuItemEvent("list_swipe", mSwipeAction, null, 0);
245042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang
2462b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        if (mSwipeAction == R.id.remove_folder) {
2472b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            FolderOperation folderOp = new FolderOperation(mFolder, false);
2482b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            HashMap<Uri, Folder> targetFolders = Folder
2492b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    .hashMapForFolders(conv.getRawFolders());
2502b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            targetFolders.remove(folderOp.mFolder.folderUri.fullUri);
2512b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            final FolderList folders = FolderList.copyOf(targetFolders.values());
2522b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            conv.setRawFolders(folders);
2532b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            final ContentValues values = new ContentValues();
2542b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            folderUris = new ArrayList<Uri>();
2552b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            folderUris.add(mFolder.folderUri.fullUri);
2562b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            adds = new ArrayList<Boolean>();
2572b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            adds.add(Boolean.FALSE);
2582b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            ConversationCursor.addFolderUpdates(folderUris, adds, values);
2592b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            ConversationCursor.addTargetFolders(targetFolders.values(), values);
2602b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            cc.mostlyDestructiveUpdate(Conversation.listOf(conv), values);
2612b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (mSwipeAction == R.id.archive) {
2622b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            cc.mostlyArchive(convList);
2632b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (mSwipeAction == R.id.delete) {
2642b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            cc.mostlyDelete(convList);
265512821c11d89d49908f3cfdee0b582601f500f3dJin Cao        } else if (mSwipeAction == R.id.discard_outbox) {
266512821c11d89d49908f3cfdee0b582601f500f3dJin Cao            cc.moveFailedIntoDrafts(convList);
2676c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        }
2689365a826b46b0e274df88e92534f7d871eef2aa2mindyp        if (mSwipedListener != null) {
2699365a826b46b0e274df88e92534f7d871eef2aa2mindyp            mSwipedListener.onListItemSwiped(convList);
2709365a826b46b0e274df88e92534f7d871eef2aa2mindyp        }
2716c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira        adapter.notifyDataSetChanged();
272ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        if (mConvCheckedSet != null && !mConvCheckedSet.isEmpty()
273ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao                && mConvCheckedSet.contains(conv)) {
274ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao            mConvCheckedSet.toggle(conv);
27554f120f6ada40c7191811dfff99b151e9e192c78mindyp            // Don't commit destructive actions if the item we just removed from
27654f120f6ada40c7191811dfff99b151e9e192c78mindyp            // the selection set is the item we just destroyed!
277ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao            if (!conv.isMostlyDead() && mConvCheckedSet.isEmpty()) {
27854f120f6ada40c7191811dfff99b151e9e192c78mindyp                commitDestructiveActions(true);
27954f120f6ada40c7191811dfff99b151e9e192c78mindyp            }
280cff4a2b2e92258c6c85ed4af15bc13101aa71170Mindy Pereira        }
281f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
282f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
283f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
284f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    public void onBeginDrag(View v) {
285f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        // We do this so the underlying ScrollView knows that it won't get
286f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        // the chance to intercept events anymore
287f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira        requestDisallowInterceptTouchEvent(true);
2883b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        cancelDismissCounter();
289e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
2907c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        // Notifies the SwipeListener that a swipe has begun.
291e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        if (mSwipeListener != null) {
292e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang            mSwipeListener.onBeginSwipe();
293e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        }
294f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
295f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
296f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    @Override
2976c72a787b58a0bc3afcb71093eddf8c29d1cf5edMindy Pereira    public void onDragCancelled(SwipeableItemView v) {
2983b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        final AnimatedAdapter adapter = getAnimatedAdapter();
2993b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        if (adapter != null) {
3003b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp            adapter.startDismissCounter();
3014d4531a63cff536d2ee1a2929d0820981df8516amindyp            adapter.cancelFadeOutLastLeaveBehindItemText();
3023b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        }
3037c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein
3047c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        // Notifies the SwipeListener that a swipe has ended.
3057c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        if (mSwipeListener != null) {
3067c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein            mSwipeListener.onEndSwipe();
3077c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        }
308f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira    }
309f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira
31007118a01f7183645957010779222b84930f75b4eMindy Pereira    /**
31107118a01f7183645957010779222b84930f75b4eMindy Pereira     * Archive items using the swipe away animation before shrinking them away.
31207118a01f7183645957010779222b84930f75b4eMindy Pereira     */
313cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook    public boolean destroyItems(Collection<Conversation> convs,
31484f7d32bdc79263004ed5241480988e02f8e618cmindyp            final ListItemsRemovedListener listener) {
31584f7d32bdc79263004ed5241480988e02f8e618cmindyp        if (convs == null) {
316cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook            LogUtils.e(LOG_TAG, "SwipeableListView.destroyItems: null conversations.");
317cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook            return false;
31884f7d32bdc79263004ed5241480988e02f8e618cmindyp        }
319a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal        final AnimatedAdapter adapter = getAnimatedAdapter();
320a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal        if (adapter == null) {
321a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal            LogUtils.e(LOG_TAG, "SwipeableListView.destroyItems: Cannot destroy: adapter is null.");
322cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook            return false;
323067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        }
32484f7d32bdc79263004ed5241480988e02f8e618cmindyp        adapter.swipeDelete(convs, listener);
325cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook        return true;
326067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira    }
327067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira
328cc2f9296ad63ed681c200feb118c5caace36e72emindyp    public int findConversation(ConversationItemView view, Conversation conv) {
329dbb587f15c723ae80edb33f65c29cc2b6c15eba0Scott Kennedy        int position = INVALID_POSITION;
330067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        long convId = conv.id;
331067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        try {
332dbb587f15c723ae80edb33f65c29cc2b6c15eba0Scott Kennedy            position = getPositionForView(view);
333067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        } catch (Exception e) {
334067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            position = INVALID_POSITION;
335599e7f8bf95d2f21a966cbff1bf72adf77a90a33Andy Huang            LogUtils.w(LOG_TAG, e, "Exception finding position; using alternate strategy");
336067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        }
337067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        if (position == INVALID_POSITION) {
338067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            // Try the other way!
339067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            Conversation foundConv;
340067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            long foundId;
341067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            for (int i = 0; i < getChildCount(); i++) {
342067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                View child = getChildAt(i);
343067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                if (child instanceof SwipeableConversationItemView) {
344067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    foundConv = ((SwipeableConversationItemView) child).getSwipeableItemView()
345067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                            .getConversation();
346067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    foundId = foundConv.id;
347067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    if (foundId == convId) {
348599e7f8bf95d2f21a966cbff1bf72adf77a90a33Andy Huang                        position = i + getFirstVisiblePosition();
349067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                        break;
350067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                    }
351067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira                }
352067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira            }
353c79aec78d06928b3fa1464d6aed60019f9b4843bMindy Pereira        }
354067ef97264240a2c3f9172a9ab88ba43fef7c475Mindy Pereira        return position;
35507118a01f7183645957010779222b84930f75b4eMindy Pereira    }
35607118a01f7183645957010779222b84930f75b4eMindy Pereira
3570fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira    private AnimatedAdapter getAnimatedAdapter() {
3580fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira        return (AnimatedAdapter) getAdapter();
3590fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira    }
3600fb551891757ecddccb299d9df770bb1675e3fd2Mindy Pereira
3618937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    @Override
3628937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    public boolean performItemClick(View view, int pos, long id) {
3636126d72ae2769bd39451872f45781cadb5b90515Mark Wei        // Superclass method modifies the selection set
364f69d0394f7de5c0681b8d5d5ca0c60b55c55f815Vikram Aggarwal        final boolean handled = super.performItemClick(view, pos, id);
3656126d72ae2769bd39451872f45781cadb5b90515Mark Wei
36600ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira        // Commit any existing destructive actions when the user selects a
36700ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira        // conversation to view.
368c6adce3cf6887c4c6dd5005724565702751843d0mindyp        commitDestructiveActions(true);
369dd9d3a16148b178cb95fd4db4168a67c64eb944fMindy Pereira        return handled;
3708937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira    }
3715254486a8c37cdf2b579aa7a3b9c8eb7324062bcmindyp
3725254486a8c37cdf2b579aa7a3b9c8eb7324062bcmindyp    @Override
373d064972c4a032f3c5cfcb236aa470273f7ac2ce0mindyp    public void onScroll() {
374d064972c4a032f3c5cfcb236aa470273f7ac2ce0mindyp        commitDestructiveActions(true);
3755254486a8c37cdf2b579aa7a3b9c8eb7324062bcmindyp    }
3769365a826b46b0e274df88e92534f7d871eef2aa2mindyp
3779365a826b46b0e274df88e92534f7d871eef2aa2mindyp    public interface ListItemsRemovedListener {
3789365a826b46b0e274df88e92534f7d871eef2aa2mindyp        public void onListItemsRemoved();
3799365a826b46b0e274df88e92534f7d871eef2aa2mindyp    }
3809365a826b46b0e274df88e92534f7d871eef2aa2mindyp
3819365a826b46b0e274df88e92534f7d871eef2aa2mindyp    public interface ListItemSwipedListener {
3829365a826b46b0e274df88e92534f7d871eef2aa2mindyp        public void onListItemSwiped(Collection<Conversation> conversations);
3839365a826b46b0e274df88e92534f7d871eef2aa2mindyp    }
38469b440141f402669fe44dfd8924a94fd22ebccf7mindyp
38569b440141f402669fe44dfd8924a94fd22ebccf7mindyp    @Override
38679c3e1ed9cb1e1660c5d34b7bf3f3ab5c5dd5162Andy Huang    public final void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
387479505d71969e26b0785d8e0e1b81108731cf827Mark Wei            int totalItemCount) {
38869b440141f402669fe44dfd8924a94fd22ebccf7mindyp    }
38969b440141f402669fe44dfd8924a94fd22ebccf7mindyp
39069b440141f402669fe44dfd8924a94fd22ebccf7mindyp    @Override
39181aea35d45b3d0191ec595562a2fcf67009845d5Mark Wei    public void onScrollStateChanged(final AbsListView view, final int scrollState) {
392479505d71969e26b0785d8e0e1b81108731cf827Mark Wei        mScrolling = scrollState != OnScrollListener.SCROLL_STATE_IDLE;
393479505d71969e26b0785d8e0e1b81108731cf827Mark Wei
39448ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang        if (!mScrolling) {
39548ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            final Context c = getContext();
39648ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            if (c instanceof ControllableActivity) {
39748ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang                final ControllableActivity activity = (ControllableActivity) c;
39848ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang                activity.onAnimationEnd(null /* adapter */);
39948ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            } else {
40048ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang                LogUtils.wtf(LOG_TAG, "unexpected context=%s", c);
40148ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            }
40248ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang        }
40348ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang    }
40448ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang
40548ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang    public boolean isScrolling() {
40648ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang        return mScrolling;
40769b440141f402669fe44dfd8924a94fd22ebccf7mindyp    }
4083b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp
4093b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp    @Override
4103b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp    public void cancelDismissCounter() {
4113b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        AnimatedAdapter adapter = getAnimatedAdapter();
4123b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        if (adapter != null) {
4133b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp            adapter.cancelDismissCounter();
4143b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp        }
4153b5e82b06a0af3f5ddf3667de9a2aa26eeb5f24bmindyp    }
4167af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp
4177af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp    @Override
4187af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp    public LeaveBehindItem getLastSwipedItem() {
4197af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        AnimatedAdapter adapter = getAnimatedAdapter();
4207af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        if (adapter != null) {
4217af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp            return adapter.getLastLeaveBehindItem();
4227af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        }
4237af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp        return null;
4247af43c93d4ca90bb0a43ec790a434c95128cc99fmindyp    }
425e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
426e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    public void setSwipeListener(SwipeListener swipeListener) {
427e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        mSwipeListener = swipeListener;
428e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    }
429e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang
430e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    public interface SwipeListener {
431e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang        public void onBeginSwipe();
4327c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein        public void onEndSwipe();
433e6f4ddc19c205796cde91d58483fa39c3cc13f1aAlice Yang    }
43400ffece08e94ff5774b2a53c0adeb2f3d0815d66Mindy Pereira}
435