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