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