SwipeableListView.java revision 489dd22c64c718b6953b4bd6acef925e82c53c87
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 20f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.content.Context; 21f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.util.AttributeSet; 22f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.view.MotionEvent; 23f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.view.View; 24489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huangimport android.widget.ListAdapter; 25f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport android.widget.ListView; 26f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 27f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.R; 28f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.browse.ConversationItemView; 29f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.providers.Conversation; 30f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.ui.SwipeHelper.Callback; 31f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.android.mail.ui.UndoBarView.UndoListener; 32489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huangimport com.android.mail.utils.LogUtils; 33489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang 34f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport com.google.common.collect.ImmutableList; 35f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 36f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport java.util.Collection; 37f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 38f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereirapublic class SwipeableListView extends ListView implements Callback { 39f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira private SwipeHelper mSwipeHelper; 40f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira private SwipeCompleteListener mSwipeCompleteListener; 4119bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira // TODO(mindyp) disable for original droidfood build. 4219bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira private boolean ENABLE_SWIPE = false; 43489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang private ListAdapter mDebugAdapter; 44489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang private int mDebugLastCount; 45489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang 46489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang // TODO: remove me and all enclosed blocks when b/6255909 is fixed 47489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang private static final boolean DEBUG_LOGGING_CONVERSATION_CURSOR = true; 48489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang 49489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang public static final String LOG_TAG = new LogUtils().getLogTag(); 50f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 51f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public SwipeableListView(Context context) { 52f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira this(context, null); 53f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 54f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 55f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public SwipeableListView(Context context, AttributeSet attrs) { 56f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira this(context, attrs, -1); 57f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 58f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 59f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public SwipeableListView(Context context, AttributeSet attrs, int defStyle) { 60f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira super(context, attrs, defStyle); 61f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira float densityScale = getResources().getDisplayMetrics().density; 62f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, densityScale); 63f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 64f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 65f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public void setSwipeCompleteListener(SwipeCompleteListener listener) { 66f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira mSwipeCompleteListener = listener; 67f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 68f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 69f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 70f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public boolean onInterceptTouchEvent(MotionEvent ev) { 7119bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira if (ENABLE_SWIPE) { 7219bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira return mSwipeHelper.onInterceptTouchEvent(ev) 7319bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira || super.onInterceptTouchEvent(ev); 7419bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira } else { 7519bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira return super.onInterceptTouchEvent(ev); 7619bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira } 77f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 78f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 79f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 80f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public boolean onTouchEvent(MotionEvent ev) { 8119bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira if (ENABLE_SWIPE) { 8219bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); 8319bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira } else { 8419bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira return super.onTouchEvent(ev); 8519bf5f5a4944f5bb4e62e1f32c47dce87f662cbdMindy Pereira } 86f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 87f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 88f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 89489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang public void setAdapter(ListAdapter adapter) { 90489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang super.setAdapter(adapter); 91489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang if (DEBUG_LOGGING_CONVERSATION_CURSOR) { 92489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang mDebugAdapter = adapter; 93489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang } 94489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang } 95489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang 96489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang @Override 97489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 98489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang if (DEBUG_LOGGING_CONVERSATION_CURSOR) { 99489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang final int count = mDebugAdapter == null ? 0 : mDebugAdapter.getCount(); 100489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang if (count != mDebugLastCount) { 101489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang LogUtils.i(LOG_TAG, "Conversation ListView about to change mItemCount to: %d", 102489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang count); 103489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang mDebugLastCount = count; 104489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang } 105489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang } 106489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang super.onMeasure(widthMeasureSpec, heightMeasureSpec); 107489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang } 108489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang 109489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang @Override 110489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang protected void layoutChildren() { 111489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang if (DEBUG_LOGGING_CONVERSATION_CURSOR) { 112489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang LogUtils.i(LOG_TAG, "Conversation ListView may compare last mItemCount to new val: %d", 113489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang mDebugAdapter == null ? 0 : mDebugAdapter.getCount()); 114489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang } 115489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang super.layoutChildren(); 116489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang } 117489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang 118489dd22c64c718b6953b4bd6acef925e82c53c87Andy Huang @Override 119f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public View getChildAtPosition(MotionEvent ev) { 120f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira // find the view under the pointer, accounting for GONE views 121f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira final int count = getChildCount(); 122f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira int y = 0; 123f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira int touchY = (int) ev.getY(); 124f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira int childIdx = 0; 125f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira View slidingChild; 126f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira for (; childIdx < count; childIdx++) { 127f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira slidingChild = getChildAt(childIdx); 128f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira if (slidingChild.getVisibility() == GONE) { 129f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira continue; 130f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 131f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira y += slidingChild.getMeasuredHeight(); 132f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira if (touchY < y) return slidingChild; 133f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 134f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira return null; 135f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 136f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 137f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 138f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public View getChildContentView(View v) { 139f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira return v; 140f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 141f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 142f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 143f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public boolean canChildBeDismissed(View v) { 1446529023dc8c204bfd83a3805158342d1a5e21f43Mindy Pereira return v instanceof ConversationItemView; 145f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 146f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 147f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 148f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public void onChildDismissed(View v) { 149f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira if (v instanceof ConversationItemView) { 150f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira Conversation c = ((ConversationItemView) v).getConversation(); 151f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira c.position = getPositionForView(v); 152f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira AnimatedAdapter adapter = ((AnimatedAdapter) getAdapter()); 153f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira final ImmutableList<Conversation> conversations = ImmutableList.of(c); 154f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira adapter.delete(conversations, new ActionCompleteListener() { 155f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 156f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public void onActionComplete() { 157f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira mSwipeCompleteListener.onSwipeComplete(conversations); 158f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 159f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira }); 160f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 161f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 162f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 163f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 164f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public void onBeginDrag(View v) { 165f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira // We do this so the underlying ScrollView knows that it won't get 166f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira // the chance to intercept events anymore 167f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira requestDisallowInterceptTouchEvent(true); 168f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira v.setActivated(true); 169f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 170f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 171f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira @Override 172f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public void onDragCancelled(View v) { 173f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira v.setActivated(false); 174f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 175f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira 176f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public interface SwipeCompleteListener { 177f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira public void onSwipeComplete(Collection<Conversation> conversations); 178f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira } 179f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira} 180